最後更新: 2015-08-04


  • 介紹
  • Install
  • 運作
  • Use SSH Keys For Authentication
  • Testing Example
  • Manage Your Inventory
  • sudo
  • Configuration File
  • Modules
  • Debug
  • Speedup Ansible
  • Playbooks
  • DOC


ansible 是一個 automation engine

* no agents ( 因為它是用 ssh 的)
* no additional custom security infrastructure ( ssh key login )
* automation jobs (Playbooks - YAML)



* Each playbook is composed of one or more ‘plays’ in a list.
* The goal of a play is to map a group of hosts to some well defined "roles"
* Each play contains a list of tasks.




Ubuntu PPA:

add-apt-repository ppa:rquillo/ansible
apt-get update
apt-get install ansible


apt-get install sshpass

apt-get install python-pip

pip install ansible markupsafe



Ansible 會有這樣的 File Upload 到 Client:




被 upload 的 script 叫 "Ansible Modules"

upload 完後, Ansible 就會用 ssh 行它


Use SSH Keys For Authentication


# set up SSH agent to avoid retyping passwords

ssh-agent bash

ssh-add ~/.ssh/id_rsa


Manage Your Inventory



# 沒有 Group 的 host

[webservers] ansible_ssh_host= ansible_ssh_port=5555


[dbservers]    ansible_connection=ssh    ansible_ssh_user=test1    ansible_connection=ssh    ansible_ssh_user=test1
# OR
# ansible_ssh_user=test1


Other Host Variables:

    ansible_ssh_pass                                    # --ask-pass (-k)
    ansible_sudo_pass                                  # --ask-sudo-pass


Testing Example:


# Now ping all your nodes:

ansible all -m ping

lamp | success >> {
    "changed": false,
    "ping": "pong"
mailscanner | FAILED => Authentication failure.


# Now run a live command on all of your nodes:

ansible all -a "/bin/echo hello"

加速 ansible

停用 Host Key Checking:

# reasonably slow


host_key_checking = False

Ad-Hoc Commands:



-k, --ask-pass                         # must install the sshpass program (apt-get install sshpass)
-K, --ask-sudo-pass                 # If using sudo features and when sudo requires a password


-s, --sudo                               # to access sudo mode

-t TREE, --tree=TREE

--list-hosts                            # outputs a list of matching hosts; does not execute anything else

-i INVENTORY,                       # (default=/etc/ansible/hosts)

-l SUBSET, --limit=SUBSET    # further limit selected hosts to an additional pattern


# running from another user account

ansible atlanta  -f 10 -a "/usr/bin/foo" -u username

* 10 simultaneous processes to use (default: 5)


shell module

ansible raleigh -m shell -a 'echo $TERM'

* If we want to execute a module using a shell

yum module

# 在 上以 yum 安裝 httpd

* state=present|latest|absent

absent => remove
present => install
latest  => upgrade

ansible -m yum  -a "name=httpd state=present"

File Transfer:(file module)

ansible atlanta -m copy -a "src=/etc/hosts dest=/tmp/hosts"

ansible webservers -m file -a "dest=/srv/foo/b.txt mode=600 owner=mdehaan group=mdehaan"

File Permission (file module)

# Grant user Joe read access to a file
name=/etc/foo.conf entity=joe etype=user permissions="r" state=present


\s               # it matches any whitespace character, this is equivalent to the set [ \t\n\r\f\v]

# backup=yes:
# state=present: 當不中 "regexp"時, 亦會加這行. 這行係加在檔尾的
# state=absent: remove 中 regexp 的那行

# create: the file will be created if it does not already exist


*** Disable selinux

- lineinfile: dest=/etc/selinux/config regexp=^SELINUX=enforcing line=SELINUX=disabled

- lineinfile: dest=/etc/sudoers state=absent regexp="^%wheel"

- lineinfile: dest=/etc/hosts regexp='^127\.0\.0\.1' line=' localhost'

# Add a line to a file if it does not exist, without passing regexp
- lineinfile: dest=/tmp/testfile line=" foo"

- lineinfile: dest=/etc/postfix/ regexp='myhostname' replace='myhostname={{ droplet }}' backup=yes


# Default: backup=no
# regexp: Uses Python regular expressions (
# replace: backreferences(\1 , \2) that will get expanded with the regexp capture groups if the regexp matches

- replace: dest=/etc/hosts regexp='(\s+)old\.host\.name(\s+.*)?$' replace='\\2' backup=yes

Replace String

# backup=no

replace: dest=/etc/hosts regexp='(\s+)old\.host\.name(\s+.*)?$' replace='\\2' backup=yes

Managing Services:


* started/stopped are idempotent actions that will not run commands unless necessary

# restart service

ansible webservers -m service -a "name=httpd state=restarted"

* restarted will always bounce the service.

# enable service

ansible webservers -m service -a "name=httpd enabled=yes"

Gathering Facts:

ansible all -m setup

Users and Groups:

* uid
* comment
* append=no         # only add groups
* home
* createhome=yes
* group=""          # Optionally sets the user's primary group
* groups=""         # Puts the user in this comma-delimited list of groups
* state=present     # taking action if the state is different from what is stated.
* system=no
* shell=""

# Remove the user 'tester'
- user: name=tester state=absent remove=yes

ansible all -m user -a "name=foo password=<crypted password here>"

建立 crypted password:

# generate crypted passwords for the user module
# -m, --method=TYPE
# mkpasswd -m help

mkpasswd --method=sha-512



當 guest 有用 selinux 時, 那一定要安裝 "libselinux-python" package

# 在 guest

yum install libselinux-python

# ansible

# install libselinux-python
  - name: install libselinux-python
    yum: name=libselinux-python state=present
  - lineinfile: dest=/etc/selinux/config regexp=^SELINUX=enforcing line=SELINUX=disabled
  - command: setenforce 0
      removes: /selinux/enforce



ansible all -m ping -u bruce --sudo

在用 --sudo (-s) 時, 會有以下 default value

  • sudo_user=root
  • sudo_exe=sudo
  • sudo_flags=-H

# The default is "-H" which preserves the environment of the original user.


ansible -vvv sshgw -m ping -s -K

<> EXEC ['sshpass', '-d6', 'ssh', '-tt', '-q', '-o', .... "/bin/sh -c 'mkdir -p $HOME/.ansible/tmp/ansible-1389164997.16-134005641234727 ...'"]
<> PUT /tmp/tmpY4V4M2 TO /home/ansible/.ansible/tmp/ansible-1388998234.57-103349027143503/ping
<> PUT /tmp/tmpTKCHln TO /home/ansible/.ansible/tmp/ansible-1388998234.57-103349027143503/arguments
<> EXEC ['sshpass', '-d6', 'ssh', '-tt', '-q', '-o', ...  -u root /bin/sh -c ... /usr/bin/python ...

那 user 要可以 sudo /bin/shmaster 要安 sshpass

# 當沒有 NOPASSWD: 時, 那要用 -K
Host_Alias      OFFICE=sshgw
ansible         OFFICE=(root)   NOPASSWD:       /bin/sh


Configuration File


hostfile = /etc/ansible/hosts
action_plugins = /usr/share/ansible_plugins/action_plugins
# default SSH timeout to use on connection attempts
timeout = 10

accelerate_port = 5099



* All modules technically return JSON format data


加 Client:

# add host to group 'just_created' with variable foo=42

- add_host: name={{ ip_from_ec2 }} groups=just_created foo=42

# add a host with a non-standard port local to your machines

- add_host: name={{ new_ip }}:{{ new_port }}

# add a host alias that we reach through a tunnel

- add_host: hostname={{ new_ip }}
            ansible_ssh_host={{ inventory_hostname }}
            ansible_ssh_port={{ new_port }}


firewalld: port=8081/tcp permanent=true state=disabled

Run cmd


*  variables like $HOME and operations like "<", ">", "|", and "&" will not work in command.


ansible lamp -m command -a 'iptables -nL'


# Run the command if the specified file does not exist.(creates)

- command: /usr/bin/ arg1 arg2 creates=/path/to/database

# You can also use the 'args' form to provide the options.

# This command will change the working directory to somedir/ and
# will only run when /path/to/database doesn't exist.

- command: /usr/bin/ arg1 arg2
    chdir: somedir/
    creates: /path/to/database

* creates         # a filename or glob pattern, when it already exists, this step will not be run.

* chdir            # cd into this directory before running the command

* removes       # when it does not exist, this step will not be run

Error Handling In Playbooks

- name: this will not be counted as a failure
  command: /bin/false
  ignore_errors: yes

shell module:

command through a shell (/bin/sh)



- shell: >> somelog.txt


- shell: >> somelog.txt
    chdir: somedir/
    creates: somelog.txt


removes: filename         # 當 filename 不在時, 不會 run
creates: filename         # 當 filename 存在時, 不會 run
chdir: /selinux           # cd into this directory
executable: /bin/bash     # 轉用另一個 shell



Step1: ping

ansible sshgw -m ping

sshgw | success >> {
    "changed": false,
    "ping": "pong"

Step2: -vvv


ansible -vvv sshgw -m ping -s

Step3: debug module

Print statements during execution

- debug: msg="System {{ inventory_hostname }} has uuid {{ ansible_product_uuid }}"

TASK: [debug msg="Install useful package"] ************************************
ok: [x.y.z] => {
    "msg": "Install useful package"



Speedup Ansible


在 Ansible > 1.5 就可以不用 "accelerated mode"了, 因為有 SSH pipelining (2-6x faster) 了

SSH pipelining 原理

* reduces the number of SSH operations required to execute a module
* when using "sudo:", you must first disable "requiretty" in /etc/sudoers

SSH pipelining Setting

# Default: False


在 Enterprise Linux 6 上的 OpenSSH 是用不到的.

所以 Ansible 會用 pure-python SSH client (paramiko) 去 connect guest

Accelerated Mode 原理

ControlPersist (10x faster)

launching a temporary daemon(port 5099) over SSH

* uses SSH for initial secure key exchange( AES key)

* daemon will accept connections for 30 minutes)

Guest 要 requirement

* python 2.5

* python-keyczar (centos6 default 沒有這個 package)

Accelerated Mode Setting

# controller

# port to use for accelerated mode
accelerate_port = 5099

# A keepalive packet is sent back to the controller every 15 seconds
accelerate_timeout = 30


- hosts: all
  accelerate: true
  timeout: 300
  minutes: 30
# accelerate_port: 5099

sudo 的限制

You must remove requiretty from your sudoers options.
the NOPASSWD option is required for sudo’ed commands.





modules: can be executed directly on remote hosts or through Playbooks

Each module supports taking arguments. Nearly all modules take key=value arguments


ansible-playbook --version

ansible-playbook 1.5.4






-s, --sudo            run operations with sudo (nopasswd)
-K, --ask-sudo-pass   ask for sudo password

--step                one-step-at-a-time: confirm each task before running

Run Script(yml):

ansible-playbook --syntax-check server.yml

ansible-playbook server.yml

當 yml 沒有指定 hosts 時

- hosts: x.y.z

ansible-playbook -i hosts server.yml


YAML Syntax

# indicates the start of a document

# Members of a list, must same indentation level
- Apple
- Orange
- Strawberry

# A dictionary, key: value

name: Example Developer
job: Developer
skill: Elite

# 相等

{name: Example Developer, job: Developer, skill: Elite}

Example (建立一個 smtp server)

file: hosts


file: server.yml

# separated by colons
- hosts: x.y.z

# Variables, call by {{ droplet }}
    droplet: x.y.z

# running things as another user
  remote_user: root
  sudo: yes


# create user
  - name: create user
    user: name=smtpuser password="xxxx"

# iptables
  - name: config iptables
    copy: src=/root/config_file/iptables dest=/etc/sysconfig/iptables
  - name: config iptables
    service: name=iptables state=restarted

# dovecot
  - name: install dovecot
    yum: name=dovecot state=present
  # If the action line is getting too long for comfort you can
  # break it on a space and indent any continuation lines
  - name: copy config file
    copy: src=/root/config_file/10-master.conf
  - name: restart dovecot
    service: name=dovecot state=restarted
  - name: auto start dovecot
    service: name=dovecot enabled=yes

# postfix
  - copy: src=/root/config_file/ dest=/etc/postfix
  - lineinfile: dest=/etc/postfix/ regexp='^myhostname' line='myhostname = {{ droplet }}' backup=yes
  - copy: src=/root/config_file/ dest=/etc/postfix
  - service: name=postfix state=restarted
  - service: name=postfix enabled=yes