Ansible hors des sentiers battus Aurélien Maury
#DevoxxFR #ansibleRocks
1
whoami
#DevoxxFR #ansibleRocks
2
whoami
#DevoxxFR #ansibleRocks
2
uname
@YesWeScale
#DevoxxFR #ansibleRocks
3
Agenda
#DevoxxFR #ansibleRocks
4
Elevator pitch
#DevoxxFR #ansibleRocks
5
Python
#DevoxxFR #ansibleRocks
6
Orchestration
#DevoxxFR #ansibleRocks
7
Configuration
#DevoxxFR #ansibleRocks
8
Idempotence
#DevoxxFR #ansibleRocks
9
Sans agent
#DevoxxFR #ansibleRocks
10
SSH
#DevoxxFR #ansibleRocks
11
« Getting started »
#DevoxxFR #ansibleRocks
12
« Getting started »
ansible-playbook
#DevoxxFR #ansibleRocks
12
OK !
#DevoxxFR #ansibleRocks
13
OK !
#DevoxxFR #ansibleRocks
13
SSH-fu
#DevoxxFR #ansibleRocks
14
SSH-fu # $HOME/.ssh/config Host bastion Hostname User IdentityFile
84.39.41.33 admin ~/.ssh/bastion_key
Host 192.168.47.* ProxyCommand ssh -W %h:%p bastion User admin IdentityFile ~/.ssh/zone_key Host * ControlMaster ControlPath ControlPersist
#DevoxxFR #ansibleRocks
auto ~/.ssh/mux-%r@%h:%p 15m
15
SSH-fu # $HOME/.ssh/config Host bastion Hostname User IdentityFile
84.39.41.33 admin ~/.ssh/bastion_key
Host 192.168.47.* ProxyCommand ssh -W %h:%p bastion User admin IdentityFile ~/.ssh/zone_key Host * ControlMaster ControlPath ControlPersist
#DevoxxFR #ansibleRocks
auto ~/.ssh/mux-%r@%h:%p 15m
16
SSH-fu # $HOME/.ssh/config Host bastion Hostname User IdentityFile
84.39.41.33 admin ~/.ssh/bastion_key
Host 192.168.47.* ProxyCommand ssh -W %h:%p bastion User admin IdentityFile ~/.ssh/zone_key Host * ControlMaster ControlPath ControlPersist
#DevoxxFR #ansibleRocks
auto ~/.ssh/mux-%r@%h:%p 15m
17
SSH-fu # $WORKSPACE/ssh.cfg Host bastion Hostname User IdentityFile
84.39.41.33 admin ~/.ssh/bastion_key
Host 192.168.47.* ProxyCommand ssh -W %h:%p -F ssh.cfg bastion User admin IdentityFile ~/.ssh/zone_key Host * ControlMaster ControlPath ControlPersist
#DevoxxFR #ansibleRocks
auto ~/.ssh/mux-%r@%h:%p 15m
18
Hosts
#DevoxxFR #ansibleRocks
19
Host Inventory [web_servers] 192.168.47.11 192.168.47.12 192.168.47.13 [db_servers] 192.168.47.10 [production:children] web_servers db_servers [production:vars] ansible_ssh_user=admin ansible_ssh_private_key_file=/home/ops/.ssh/id_rsa.prod
#DevoxxFR #ansibleRocks
20
Dynamic Inventory
#DevoxxFR #ansibleRocks
21
Dynamic Inventory
ansible --inventory-file=./static_inventory
#DevoxxFR #ansibleRocks
21
Dynamic Inventory
ansible --inventory-file=./static_inventory
ansible --inventory-file=./executable_returning_json
#DevoxxFR #ansibleRocks
21
Dynamic Inventory --list { "databases" : { "hosts" : [ "host1.example.com", "host2.example.com" ], "vars" : { "a" : true } }, "webservers" : [ "host2.example.com", "host3.example.com" ], "atlanta" : { "hosts" : [ "host1.example.com", "host4.example.com"], "vars" : { "b" : false }, "children": [ "marietta", "5points" ] }, "marietta" : [ "host6.example.com" ] }
#DevoxxFR #ansibleRocks
22
Dynamic Inventory --host $HOST { "favcolor" : "red", "ntpserver" : "wolf.example.com", "monitoring" : "pack.example.com" }
#DevoxxFR #ansibleRocks
23
Dynamic Inventory
#DevoxxFR #ansibleRocks
24
One more thing
La confiance n’exclut pas le contrôle.
#DevoxxFR #ansibleRocks
25
Modules
#DevoxxFR #ansibleRocks
26
Il y a sûrement un module pour ça
#DevoxxFR #ansibleRocks
27
Il y a sûrement un module pour ça Cloud - Amazon, Cloudstack, Openstack, VMWare, DO
#DevoxxFR #ansibleRocks
27
Il y a sûrement un module pour ça Cloud - Amazon, Cloudstack, Openstack, VMWare, DO Clustering - consul, zookeeper
#DevoxxFR #ansibleRocks
27
Il y a sûrement un module pour ça Cloud - Amazon, Cloudstack, Openstack, VMWare, DO Clustering - consul, zookeeper Commands - shell, script, expect
#DevoxxFR #ansibleRocks
27
Il y a sûrement un module pour ça Cloud - Amazon, Cloudstack, Openstack, VMWare, DO Clustering - consul, zookeeper Commands - shell, script, expect Database - mysql, postgresql, redis, riak
#DevoxxFR #ansibleRocks
27
Il y a sûrement un module pour ça Cloud - Amazon, Cloudstack, Openstack, VMWare, DO Clustering - consul, zookeeper Commands - shell, script, expect Database - mysql, postgresql, redis, riak Files - copy, fetch, lineinfile, template, unarchive
#DevoxxFR #ansibleRocks
27
Il y a sûrement un module pour ça Cloud - Amazon, Cloudstack, Openstack, VMWare, DO Clustering - consul, zookeeper Commands - shell, script, expect Database - mysql, postgresql, redis, riak Files - copy, fetch, lineinfile, template, unarchive Messaging - rabbitmq_[binding, exchange, queue, ...]
#DevoxxFR #ansibleRocks
27
Il y a sûrement un module pour ça Cloud - Amazon, Cloudstack, Openstack, VMWare, DO Clustering - consul, zookeeper Commands - shell, script, expect Database - mysql, postgresql, redis, riak Files - copy, fetch, lineinfile, template, unarchive Messaging - rabbitmq_[binding, exchange, queue, ...] Monitoring - zabbix, nagios, sensu, monit, datadog
#DevoxxFR #ansibleRocks
27
Il y a sûrement un module pour ça Cloud - Amazon, Cloudstack, Openstack, VMWare, DO Clustering - consul, zookeeper Commands - shell, script, expect Database - mysql, postgresql, redis, riak Files - copy, fetch, lineinfile, template, unarchive Messaging - rabbitmq_[binding, exchange, queue, ...] Monitoring - zabbix, nagios, sensu, monit, datadog Network - A10, F5, Openswitch, cumulus, get_url
#DevoxxFR #ansibleRocks
27
Il y a sûrement un module pour ça Cloud - Amazon, Cloudstack, Openstack, VMWare, DO Clustering - consul, zookeeper Commands - shell, script, expect Database - mysql, postgresql, redis, riak Files - copy, fetch, lineinfile, template, unarchive Messaging - rabbitmq_[binding, exchange, queue, ...] Monitoring - zabbix, nagios, sensu, monit, datadog Network - A10, F5, Openswitch, cumulus, get_url Notification - jabber, slack, mail, irc, hipchat
#DevoxxFR #ansibleRocks
27
Il y a sûrement un module pour ça Cloud - Amazon, Cloudstack, Openstack, VMWare, DO Clustering - consul, zookeeper Commands - shell, script, expect Database - mysql, postgresql, redis, riak Files - copy, fetch, lineinfile, template, unarchive Messaging - rabbitmq_[binding, exchange, queue, ...] Monitoring - zabbix, nagios, sensu, monit, datadog Network - A10, F5, Openswitch, cumulus, get_url Notification - jabber, slack, mail, irc, hipchat Packaging - apt, yum, pip, bower, npm, homebrew
#DevoxxFR #ansibleRocks
27
Il y a sûrement un module pour ça Cloud - Amazon, Cloudstack, Openstack, VMWare, DO Clustering - consul, zookeeper Commands - shell, script, expect Database - mysql, postgresql, redis, riak Files - copy, fetch, lineinfile, template, unarchive Messaging - rabbitmq_[binding, exchange, queue, ...] Monitoring - zabbix, nagios, sensu, monit, datadog Network - A10, F5, Openswitch, cumulus, get_url Notification - jabber, slack, mail, irc, hipchat Packaging - apt, yum, pip, bower, npm, homebrew Source control - git, gitlab, hg, subversion
#DevoxxFR #ansibleRocks
27
Il y a sûrement un module pour ça Cloud - Amazon, Cloudstack, Openstack, VMWare, DO Clustering - consul, zookeeper Commands - shell, script, expect Database - mysql, postgresql, redis, riak Files - copy, fetch, lineinfile, template, unarchive Messaging - rabbitmq_[binding, exchange, queue, ...] Monitoring - zabbix, nagios, sensu, monit, datadog Network - A10, F5, Openswitch, cumulus, get_url Notification - jabber, slack, mail, irc, hipchat Packaging - apt, yum, pip, bower, npm, homebrew Source control - git, gitlab, hg, subversion System - known_host, authorized_key, user, group
#DevoxxFR #ansibleRocks
27
Il y a sûrement un module pour ça Cloud - Amazon, Cloudstack, Openstack, VMWare, DO Clustering - consul, zookeeper Commands - shell, script, expect Database - mysql, postgresql, redis, riak Files - copy, fetch, lineinfile, template, unarchive Messaging - rabbitmq_[binding, exchange, queue, ...] Monitoring - zabbix, nagios, sensu, monit, datadog Network - A10, F5, Openswitch, cumulus, get_url Notification - jabber, slack, mail, irc, hipchat Packaging - apt, yum, pip, bower, npm, homebrew Source control - git, gitlab, hg, subversion System - known_host, authorized_key, user, group Windows - trucs windows inconnus de moi
#DevoxxFR #ansibleRocks
27
Appel à un module - name: un joli titre c’est mieux apt: pkg="tmux" state=present update_cache=yes - name: un joli titre c’est mieux apt: pkg="screen" state=present
#DevoxxFR #ansibleRocks
28
with_items - name: un joli titre c’est mieux apt: pkg="{{ item }}" state=present update_cache=yes with_items: - tmux - screen
#DevoxxFR #ansibleRocks
29
with_items - name: add several users user: name={{ item.name }} state=present groups={{ item.groups }} with_items: - { name: 'testuser1', groups: 'wheel' } - { name: 'testuser2', groups: 'root' }
#DevoxxFR #ansibleRocks
30
with_* with_file: - file_1 - file_2 with_fileglob: - files/*.yml with_together: - ['a','b'] - [1,2] with_subelements, with_sequence, with_random_choice, with_indexed_items, with_dict
#DevoxxFR #ansibleRocks
31
Donnez vie à vos snippets
#DevoxxFR #ansibleRocks
32
Donnez vie à vos snippets Faire un module Ansible ?
#DevoxxFR #ansibleRocks
32
Donnez vie à vos snippets Faire un module Ansible ? 15 lignes de Python
#DevoxxFR #ansibleRocks
32
Donnez vie à vos snippets Faire un module Ansible ? 15 lignes de Python Installer un module Ansible ?
#DevoxxFR #ansibleRocks
32
Donnez vie à vos snippets Faire un module Ansible ? 15 lignes de Python Installer un module Ansible ? posez le dans ./library
#DevoxxFR #ansibleRocks
32
Roles
#DevoxxFR #ansibleRocks
33
Structure . ├── ├── │ ├── ├── │ ├── │ ├── │ ├── ├── │ │ └──
README.md defaults └── main.yml files handlers └── main.yml meta └── main.yml tasks └── main.yml templates tests ├── inventory └── test.yml vars └── main.yml
#DevoxxFR #ansibleRocks
--> variables par défaut --> fichiers statiques --> handlers --> fiche d'info et dépendances --> tâches (appels de modules) --> templates Jinja2
--> variables fortes
34
Structure . ├── ├── │ ├── ├── │ ├── │ ├── │ ├── ├── │ │ └──
README.md defaults └── main.yml files handlers └── main.yml meta └── main.yml tasks └── main.yml templates tests ├── inventory └── test.yml vars └── main.yml
--> variables par défaut --> fichiers statiques --> handlers --> fiche d'info et dépendances --> tâches (appels de modules) --> templates Jinja2
--> variables fortes
ansible-galaxy init mon_role_amoi #DevoxxFR #ansibleRocks
34
ansible-galaxy
#DevoxxFR #ansibleRocks
35
ansible-galaxy
https://galaxy.ansible.com
#DevoxxFR #ansibleRocks
35
ansible-galaxy
https://galaxy.ansible.com
ansible-galaxy install yatesr.timezone
#DevoxxFR #ansibleRocks
35
ansible-galaxy # requirements.yml - src: yatesr.timezone - src: https://github.com/bennojoy/nginx - src: https://github.com/bennojoy/nginx version: master name: nginx_role - src: https://some.webserver.example.com/files/master.tar.gz name: http-role
#DevoxxFR #ansibleRocks
36
ansible-galaxy # requirements.yml - src: yatesr.timezone - src: https://github.com/bennojoy/nginx - src: https://github.com/bennojoy/nginx version: master name: nginx_role - src: https://some.webserver.example.com/files/master.tar.gz name: http-role
ansible-galaxy install -r requirements.yml
#DevoxxFR #ansibleRocks
36
Variables
#DevoxxFR #ansibleRocks
37
YAML —-simplest_num: 42 simplest_str: "Terry Pratchett" # Qui lit les commentaires de toutes façons ? some_list: - "DON'T THINK OF IT AS DYING" - "JUST THINK OF IT AS" - "LEAVING EARLY TO AVOID THE RUSH" some_dict: key: "value" other_key: 10 list_of_dict: - { indentation: "is", the_key: "si si" }
#DevoxxFR #ansibleRocks
38
YAML —-simplest_num: 42 simplest_str: "Terry Pratchett" # Qui lit les commentaires de toutes façons ? some_list: - "DON'T THINK OF IT AS DYING" - "JUST THINK OF IT AS" - "LEAVING EARLY TO AVOID THE RUSH" some_dict: key: "value" other_key: 10 list_of_dict: - { indentation: "is", the_key: "si si" }
#DevoxxFR #ansibleRocks
39
YAML —-simplest_num: 42 simplest_str: "Terry Pratchett" # Qui lit les commentaires de toutes façons ? some_list: - "DON'T THINK OF IT AS DYING" - "JUST THINK OF IT AS" - "LEAVING EARLY TO AVOID THE RUSH" some_dict: key: "value" other_key: 10 list_of_dict: - indentation: "is" the_key: "si si" #DevoxxFR #ansibleRocks
40
YAML
{{ playbook_dir }} {{ inventory_dir }}
#DevoxxFR #ansibleRocks
41
Facts ansible -m setup localhost localhost | SUCCESS => { "ansible_facts": { "ansible_all_ipv4_addresses": [ "192.168.42.2", "192.168.99.1" ], "ansible_all_ipv6_addresses": [ "fe80::4e8d:79ff:fee8:54fe%en1", "fe80::f0ad:11ff:fee9:fcc8%awdl0" ], "ansible_architecture": "x86_64", "ansible_awdl0": { "device": "awdl0", "flags": [ "UP", "BROADCAST", "RUNNING", "PROMISC", "SIMPLEX", "MULTICAST" #DevoxxFR #ansibleRocks
42
Facts ansible_distribution ansible_distribution_major_version ansible_default_ipv4.address ansible_eth0.ipv4.address ansible_processor_cores ansible_hostname ansible_mounts ansible_interfaces [...]
#DevoxxFR #ansibleRocks
43
Fact caching # ansible.cfg [defaults] gathering = smart fact_caching = redis fact_caching_timeout = 7200
#DevoxxFR #ansibleRocks
44
Fact caching # ansible.cfg [defaults] gathering = smart fact_caching = jsonfile fact_caching_connection = /tmp/facts_cache fact_caching_timeout = 7200
#DevoxxFR #ansibleRocks
45
Set_fact
—-- hosts: target_group become: yes pre_tasks: - shell: > {{ playbook_dir }}/scripts/xml2yaml.py xml/* register: yaml_out
#DevoxxFR #ansibleRocks
46
Set_fact "--styles: - grindcore - disco - punk"
—-- hosts: target_group become: yes pre_tasks: - shell: > {{ playbook_dir }}/scripts/xml2yaml.py xml/* register: yaml_out
#DevoxxFR #ansibleRocks
46
Set_fact "--styles: - grindcore - disco - punk"
—-- hosts: target_group become: yes pre_tasks: - shell: > {{ playbook_dir }}/scripts/xml2yaml.py xml/* register: yaml_out
- set_fact: table_oauth: "{{ yaml_out.stdout | from_yaml }}"
#DevoxxFR #ansibleRocks
46
Set_fact
—-- hosts: target_group become: yes pre_tasks: - include: custom_facts.yml
#DevoxxFR #ansibleRocks
47
Précédence role defaults inventory vars inventory group_vars inventory host_vars playbook group_vars playbook host_vars host facts registered vars set_facts play vars play vars_prompt play vars_files role and include vars block vars (seulement pour les tâches du bloc) task vars (seulement pour la tâche) extra vars (ultime)
#DevoxxFR #ansibleRocks
48
Précédence role defaults inventory vars inventory group_vars inventory host_vars playbook group_vars playbook host_vars host facts registered vars set_facts play vars play vars_prompt play vars_files role and include vars block vars (seulement pour les tâches du bloc) task vars (seulement pour la tâche) extra vars (ultime)
#DevoxxFR #ansibleRocks
49
Rebonjour M. Anderson
#DevoxxFR #ansibleRocks
50
Agent-like
#DevoxxFR #ansibleRocks
51
Agent-like
ansible-playbook
#DevoxxFR #ansibleRocks
51
Agent-like
#DevoxxFR #ansibleRocks
52
Agent-like
git push
#DevoxxFR #ansibleRocks
52
Agent-like
#DevoxxFR #ansibleRocks
53
Agent-like
cron + ansible-pull
#DevoxxFR #ansibleRocks
53
Vagrant
#DevoxxFR #ansibleRocks
54
Vagrant provisioner remote config.vm.provision "ansible" do |ansible| ansible.groups = { "web_servers" => ["vm_one", "vm_two"], "db_servers" => ["vm_three"], "production:children" => [ "web_servers", "db_servers" ], "all_groups:children" => ["production"] } ansible.playbook = "upgrade_stack.yml" end
#DevoxxFR #ansibleRocks
55
Vagrant provisioner remote config.vm.provision "ansible" do |ansible| ansible.groups = { "web_servers" => ["vm_one", "vm_two"], "db_servers" => ["vm_three"], "production:children" => [ "web_servers", "db_servers" ], "all_groups:children" => ["production"] } ansible.playbook = "upgrade_stack.yml" end
#DevoxxFR #ansibleRocks
56
Vagrant provisioner remote config.vm.provision "ansible" do |ansible| ansible.groups = { "web_servers" => ["vm_one", "vm_two"], "db_servers" => ["vm_three"], "production:children" => [ "web_servers", "db_servers" ], "all_groups:children" => ["production"] } ansible.playbook = "upgrade_stack.yml" end
#DevoxxFR #ansibleRocks
57
Vagrant provisioner local config.vm.provision "ansible_local" do |ansible| ansible.playbook = "upgrade_stack.yml" end
#DevoxxFR #ansibleRocks
58
Packer
#DevoxxFR #ansibleRocks
59
Packer : Elevator pitch
#DevoxxFR #ansibleRocks
60
Packer : Elevator pitch
Boot VM • • • • • •
OpenStack AWS Docker Qemu GCE …
#DevoxxFR #ansibleRocks
60
Packer : Elevator pitch
Boot VM • • • • • •
OpenStack AWS Docker Qemu GCE …
#DevoxxFR #ansibleRocks
Provision • • • • • •
Puppet Saltstack Chef Ansible Shell …
60
Packer : Elevator pitch
Boot VM • • • • • •
OpenStack AWS Docker Qemu GCE …
#DevoxxFR #ansibleRocks
Provision • • • • • •
Puppet Saltstack Chef Ansible Shell …
60
Snapshot
build-me.json "provisioners": [
{
"type": "file",
"source": "ansible/requirements.yml",
"destination": "/tmp/requirements.yml"
},
{
"type": "shell",
"scripts": [
"scripts/ansible-seed.sh"
]
},
{
"type": "ansible-local",
"playbook_dir": "ansible",
"playbook_file": "ansible/bootstrap.yml"
} }
#DevoxxFR #ansibleRocks
61
build-me.json "provisioners": [
{
"type": "file",
"source": "ansible/requirements.yml",
"destination": "/tmp/requirements.yml"
},
{
"type": "shell",
"scripts": [
"scripts/ansible-seed.sh"
]
},
{
"type": "ansible-local",
"playbook_dir": "ansible",
"playbook_file": "ansible/bootstrap.yml"
} }
#DevoxxFR #ansibleRocks
62
build-me.json "provisioners": [
{
"type": "file",
"source": "ansible/requirements.yml",
"destination": "/tmp/requirements.yml"
},
{
"type": "shell",
"scripts": [
"scripts/ansible-seed.sh"
]
},
{
"type": "ansible-local",
"playbook_dir": "ansible",
"playbook_file": "ansible/bootstrap.yml"
} }
#DevoxxFR #ansibleRocks
63
build-me.json "provisioners": [
{
"type": "file",
"source": "ansible/requirements.yml",
"destination": "/tmp/requirements.yml"
},
{
"type": "shell",
"scripts": [
"scripts/ansible-seed.sh"
]
},
{
"type": "ansible-local",
"playbook_dir": "ansible",
"playbook_file": "ansible/bootstrap.yml"
} }
#DevoxxFR #ansibleRocks
64
Interactions
#DevoxxFR #ansibleRocks
65
Le maître mot
#DevoxxFR #ansibleRocks
66
Logique
#DevoxxFR #ansibleRocks
67
Logique
SSH => Ansible
#DevoxxFR #ansibleRocks
67
Logique
SSH => Ansible (ou Salt-SSH)
#DevoxxFR #ansibleRocks
67
Terraform
#DevoxxFR #ansibleRocks
68
Points de greffe # playbook.yml - shell: cat terraform.tfstate register: raw_tfstate - set_facts: tfstate: "{{ raw_tfstate.stdout | from_json }}"
#DevoxxFR #ansibleRocks
69
Points de greffe # playbook.yml - shell: cat terraform.tfstate register: raw_tfstate - set_facts: tfstate: "{{ raw_tfstate.stdout | from_json }}" tfstate.modules[1].resources['aws_route53_record.elb_cgate'].primary.attributes.zone_id
#DevoxxFR #ansibleRocks
69
Points de greffe
#DevoxxFR #ansibleRocks
70
Points de greffe # main.tf resource "aws_route53_record" "monitor" { zone_id = "${var.network.route53_zone_id}" name = "monitor" type = "A" ttl = "300" records = ["${aws_instance.server.private_ip}"] }
#DevoxxFR #ansibleRocks
70
Points de greffe # main.tf resource "aws_route53_record" "monitor" { zone_id = "${var.network.route53_zone_id}" name = "monitor" type = "A" ttl = "300" records = ["${aws_instance.server.private_ip}"] } output "monitor_zone_id" { value = "${aws_route53_record.monitor.zone_id}" }
#DevoxxFR #ansibleRocks
70
Points de greffe # main.tf resource "aws_route53_record" "monitor" { zone_id = "${var.network.route53_zone_id}" name = "monitor" type = "A" ttl = "300" records = ["${aws_instance.server.private_ip}"] } output "monitor_zone_id" { value = "${aws_route53_record.monitor.zone_id}" } terraform output monitor_zone_id ZSQ642E3K7JC5
#DevoxxFR #ansibleRocks
70
Points de greffe # playbook.yml - shell: cat terraform.tfstate register: raw_tfstate - set_facts: tfstate: "{{ raw_tfstate.stdout | from_json }}" tfstate.modules[1].resources['aws_route53_record.elb_cgate'].primary.attributes.zone_id
#DevoxxFR #ansibleRocks
71
Points de greffe
#DevoxxFR #ansibleRocks
72
Points de greffe # playbook.yml - shell: terraform output monitor_zone_id register: sh_monitor_zid - set_facts: monitor_zid: "{{ sh_monitor_zid.stdout }}"
#DevoxxFR #ansibleRocks
72
Pensées
#DevoxxFR #ansibleRocks
73
Métaphore
#DevoxxFR #ansibleRocks
74
Métaphore
#DevoxxFR #ansibleRocks
74
Métaphore
#DevoxxFR #ansibleRocks
74
Métaphore
+ #DevoxxFR #ansibleRocks
74
Métaphore
+
+ #DevoxxFR #ansibleRocks
74
SYSADMINS because even developers need heroes #DevoxxFR #ansibleRocks
75
#DevoxxFR #ansibleRocks
76
KEEP CALM AND HACK YOUR WAY #DevoxxFR #ansibleRocks
76
Des questions ?
#DevoxxFR #ansibleRocks
77
Merci
@YesWeScale @aurelienmaury
#DevoxxFR #ansibleRocks
78
Merci
@YesWeScale @aurelienmaury
#DevoxxFR #ansibleRocks
78