ctrl F html
執行playbook命令 node
ansible playbook -i "inventory文件名" playbook.yml f 10 (並行級別10)
加參數
python
-e "temp_file=${uuid}" 或者: --extravars "version=1.23.45 other_variable=foo" 或者: --extravars '{"pacman":"mrs","ghosts":["inky","pinky","clyde","sue"]}' 或者: --extravars "@some_file.json"
執行一個 playbook 以前,農想看看這個 playbook 的執行會影響到哪些 hosts,可以這樣作:
mysql
ansibleplaybook playbook.yml --list-hosts
實踐例子:https://github.com/ansible/ansibleexamples linux
playbook.yml 文件like 可 this:ios
---
- hosts: webservers,webservers2
vars:
http_port: 80
max_clients: 200
remote_user: root
tasks:
- name: ensure apache is at the latest version(肯定是否是最新版本)
yum: pkg=httpd state=latest
remote_user: yourname
- name: write the apache config file
template: src=/srv/httpd.j2 dest=/etc/httpd.conf
notify:
- restart apache
- name: ensure apache is running
service: name=httpd state=started
handlers:
- name: restart apache
service: name=httpd state=restarted
inventory文件例子:nginx
[all:vars]
DOCKERREGISTRY=kubernetes-master
DOCKERREGISTRYPROT=5000
IMAGESVERSION=0.0.4-SNAPSHOT
[k8s_emu_vip_node]
k8s_emu_vip_node1 ansible_ssh_host=10.222.2.201 ansible_connection=ssh ansible_ssh_user=root ansible_ssh_pass=gsta123 ansible_ssh_extra_args="-o StrictHostKeyChecking=no"
[k8s_emu_node]
k8s_emu_node1 ansible_ssh_host=1.1.1.1 ansible_connection=ssh ansible_ssh_user=root ansible_ssh_pass=gsta123 ansible_ssh_extra_args="-o StrictHostKeyChecking=no"
k8s_emu_node2 ansible_ssh_host=1.1.1.2 ansible_connection=ssh ansible_ssh_user=root ansible_ssh_pass=gsta123 ansible_ssh_extra_args="-o StrictHostKeyChecking=no"
{{}}裏進行操做,lookup插件,讀取network_test_conf文件,而後做爲變量git
{{ lookup('file', network_test_conf) | from_yaml }}
註冊node,添加主機,添加組github
- name: Register ansible node list
add_host:
hostname: '{{item["name"]}}'
groups: 'fio-server'
# ansible_ssh_host: '{{item.addresses[network_name] | selectattr("OS-EXT-IPS:type","equalto", "floating")| map(attribute="addr") | join(",")}}'
ansible_ssh_host: '{{item.addresses[network_name] | selectattr("OS-EXT-IPS:type","equalto", "fixed")| map(attribute="addr") | join(",")}}'
ansible_ssh_port: "22"
ansible_ssh_user: '{{image_ssh_user}}'
ansible_ssh_pass: '{{image_ssh_pass}}'
ansible_ssh_extra_args: "-o StrictHostKeyChecking=no"
ansible_sftp_extra_args: "-o StrictHostKeyChecking=no"
#ansible_ssh_extra_args: "-o ProxyCommand='{{proxy}}' -o StrictHostKeyChecking=no"
#ansible_sftp_extra_args: "-o ProxyCommand='{{proxy}}' -o StrictHostKeyChecking=no" with_items: "{{result.json.servers}}"
copy模塊[拷貝文件,從本地拷貝文件至各主機]web
- name: Copy the keyfile for authentication
copy: src=/wjf/weijunfeng dest={{ mongodb_datadir_prefix }}/secret owner=mongod group=mongod mode=0400
fetch模塊,從各主機拷貝文件到本地,flat: "yes"表示不自動建立目錄,若是爲false,會自動建立帶各自主機名的目錄
fetch:
src: "/home/fio-result"
dest: "./fio-test-result/{{ansible_ssh_host}}/"
flat: "yes"
建立文件或者目錄,
- name: Create data directory for mongoc configuration server
file: path={{ mongodb_datadir_prefix }}/configdb state=directory owner=mongod group=mongod
l path: 路徑,通常用於建立刪除文件或目錄
l state: file的相關操做,
directory表示建立目錄,
link表示建立軟鏈接,link還須要源路徑和目標路徑配合使用
touch表示建立文件,
absent表示刪除文件
執行shell命令
- name: Initialize the replication set
shell: /usr/bin/mongo --port "{{ mongod_port }}" /tmp/repset_init.js
暫停,等待
- name: pause pause: seconds=20
service服務模塊 好比啓動nginx
- name: Make sure nginx start with boot
service: name=nginx state=started enabled=yes
enabled:這個注意了,默認是no,若是配置成false就是restart了以後就不關心結果了,配置成yes是要關心結果。
解壓模塊
- unarchive: src=foo.tgz dest=/var/lib/foo
- unarchive: src=/tmp/foo.zip dest=/usr/local/bin copy=no
- unarchive: src=https://example.com/example.zip dest=/usr/local/bin copy=no
unarchive模塊
用於解壓文件,模塊包含以下選項:
copy:在解壓文件以前,是否先將文件複製到遠程主機,默認爲yes。若爲no,則要求目標主機上壓縮包必須存在。
creates:指定一個文件名,當該文件存在時,則解壓指令不執行
dest:遠程主機上的一個路徑,即文件解壓的路徑
grop:解壓後的目錄或文件的屬組
list_files:若是爲yes,則會列出壓縮包裏的文件,默認爲no,2.0版本新增的選項
mode:解決後文件的權限
src:若是copy爲yes,則須要指定壓縮文件的源路徑
owner:解壓後文件或目錄的屬主
yum模塊
- name: remove epel if installed
yum:
name: epel-release
state: absent
ignore_errors: true
Wait_for模塊:
等待事情發生,例如等待數據庫啓動、web容器啓動等。
- name: wait for dnsmasq port 53
wait_for:
port: 53
timeout: 10
port:等待某端口號必須啓動
path:等待某文件必須建立
host:默認是127.0.0.1,爲了知足等待其它遠程服務器的場景
timeout的單位是秒
state:默認是started,也就是等待啓動或建立,也可能存在等待刪除或中止等場景。對象是端口的時候start狀態會確保端口是打開的,stoped狀態會確認端口是關閉的;對象是文件的時候,present或者started會確認文件是存在的,而absent會確認文件是不存在的。
Git模塊
對於git版本服務的操做模塊
- name: ANSISTRANO | GIT | Update remote repository
git:
repo: "{{ ansistrano_git_repo }}"
dest: "{{ ansistrano_deploy_to }}/repo"
version: "{{ ansistrano_git_branch }}"
accept_hostkey: true
update: yes
force: yes
register: ansistrano_git_result_update
when: ansistrano_git_identity_key_path|trim == '' and ansistrano_git_identity_key_remote_path|trim == ''
repo:git倉庫的地址
dest:倉庫中的相對目錄
version:哪一個版本
accept_hostkey:若是ssh_opts包含」 -o StrictHostKeyChecking=no」,此參數能夠省略,若是配置成true或yes,須要添加hostkey
update:是否要更新新版本
force:配置成yes,本地倉庫將永遠被倉庫服務端覆蓋
get_url模塊
也就是download操做:
- name: codelivery | download | Download artifact
get_url:
url: "{{ codelivery_product_url }}"
dest: "{{ codelivery_releases_dir }}/{{ codelivery_product_url | basename }}"
force_basic_auth: "{{ codelivery_download_force_basic_auth | default(omit) }}"
headers: "{{ codelivery_download_headers | default(omit) }}"
url:http的地址
dest:下載文件到目的機的路徑
force_basic_auth:在發起請求前是否發出權限校驗信息
headers:報文頭信息
uri模塊
比get_url功能更強大的http請求模塊,能夠發起get、post、put等各類請求方式,也能夠處理返回值及內容
- name: codelivery | healthcheck | urlcheck status==200?
uri:
url: "http://{{ codelivery_urlcheck_addr }}:{{ codelivery_urlcheck_port }}{{ codelivery_urlcheck_url }}"
method: GET
headers:
Host: "{{ codelivery_urlcheck_host }}"
timeout: 10
status_code: 200
return_content: no
debug模塊 打印出變量
- debug: msg="heat_failed_reason={{reason.stdout}}"
when: result.stdout=="CREATE_FAILED"
經常使用魔數
ansible_distribution=Ubuntu
ansible_distribution_version=14.04
ansible_distribution_major_version:系統的大版本號
ansible_os_family:系統的操做系統(‘RedHat’,’Debian’,’FreeBSD’)
自定義局部變量並賦值
- name: Define nginx_user.
set_fact:
nginx_user: "{{ __nginx_user }}"
when: nginx_user is not defined
支持從 sudo 執行命令:
--- - hosts: webservers remote_user: yourname
sudo: yes
能夠登錄後,sudo 到不一樣的用戶身份,而不是使用 root:
---
- hosts: webservers
remote_user: yourname
sudo: yes
sudo_user: postgres
若是須要在使用 sudo 時指定密碼,可在運行 ansible-playbook 命令時加上選項 --ask-sudo-pass (-K). 若是使用 sudo 時,playbook 疑似被掛起,多是在 sudo prompt 處被卡住,這時可執行 Control-C 殺死卡住的任務,再從新運行一次.
service moudle
tasks:
- name: make sure apache is running
service: name=httpd state=running
command 和 shell ,它們不使用 key=value 格式的參數:[執行命令,linux命令]
tasks:
- name: disable selinux
command: /sbin/setenforce 0
command module 和 shell module 時,須要關心返回碼信息,若是有一條命令,它的成功執行的返回碼不是0, 意思就是執行命令不成功,你能夠經過下面例子進行忽略[忽略錯誤,跳過報錯]:
tasks:
- name: run this command and ignore the result
shell: /usr/bin/somecommand || /bin/true
或者:
tasks:
- name: run this command and ignore the result
shell: /usr/bin/somecommand
ignore_errors: True
假設在 ‘vars’ 那裏定義了一個變量 ‘vhost’ ,能夠這樣使用:{{}} [使用參數方法]
tasks:
- name: create a virtual host file for {{ vhost }}
template: src=somefile.j2 dest=/etc/httpd/conf.d/{{ vhost }}
‘notify’ 下列出的便是 handlers,好比當一個文件的內容被改動時,重啓兩個 services:[文件改變時執行]
- name: template configuration file
template: src=template.j2 dest=/etc/foo.conf
notify:
- restart memcached
- restart apache
Handlers 也是一些 task 的列表,經過名字來引用,它們和通常的 task 並無什麼區別.Handlers 是由通知者進行 notify, 若是沒有被 notify,handlers 不會執行.無論有多少個通知者進行了 notify,等到 play 中的全部 task 執行完成以後,handlers 也只會被執行一次,handlers 例子:
handlers:
- name: restart memcached
service: name=memcached state=restarted
- name: restart apache
service: name=apache state=restarted
- include: handlers/handlers.yml
include指令
一個 task include file foo.yml 由一個普通的 task 列表所組成,像這樣:
---
# possibly saved as tasks/foo.yml
- name: placeholder foo
command: /bin/foo
- name: placeholder bar
command: /bin/bar
Include 指令能夠跟普通的 task 混合,因此呢,你能夠這樣使用(後面能夠加參數):[include參數]
tasks: - include: tasks/foo.yml wp_user=timmy
若是Ansible 1.4 及之後的版本,include 語法可更爲精簡
tasks: - { include: wordpress.yml, wp_user: timmy, ssh_keys: [ 'keys/one.txt', 'keys/two.txt' ] }
從 1.0 版開始,Ansible 支持另外一種傳遞變量到 include files 的語法,這種語法支持結構化的變量:
tasks: - include: wordpress.yml vars: wp_user: timmy some_list_variable: - alpha - beta - gamma
Roles
Roles 基於一個已知的文件結構,去自動的加載某些 vars_files,tasks 以及 handlers。基於 roles 對內容進行分組
roles目錄結構以下:
site.yml
webservers.yml
fooservers.yml
roles/
common/
files/
templates/
tasks/
handlers/
vars/
defaults/
meta/
webservers/
files/
templates/
tasks/
handlers/
vars/
defaults/
meta/
哥哥姐姐們能夠這樣使用roles
---
- hosts: webservers
roles:
- common
- webservers
這個 playbook 爲一個角色 ‘x’ 指定了以下的行爲:
在 Ansible 1.4 及以後版本,你能夠爲」角色」的搜索設定 roles_path 配置項。使用這個配置項將全部的 common 角色 check out 到一個位置,以便在多個 playbook 項目中可方便的共享使用它們
roles_path = /opt/mysite/roles
若是 roles 目錄下有文件不存在,這些文件將被忽略。好比 roles 目錄下面缺乏了 ‘vars/’ 目錄,這也不要緊。
roles帶參數
---
- hosts: webservers
roles:
- common
- { role: foo_app_instance, dir: '/opt/a', port: 5000 }
- { role: foo_app_instance, dir: '/opt/b', port: 5001 }
roles帶條件,when
---
- hosts: webservers
roles:
- { role: some_role, when: "ansible_os_family == 'RedHat'" }
roles分配tags
---
- hosts: webservers
roles:
- { role: foo, tags: ["bar", "baz"] }
若是 play 仍然包含有 ‘tasks’ section,這些 tasks 將在全部 roles 應用完成以後才被執行。
若是你但願定義一些 tasks,讓它們在 roles 以前以及以後執行,大佬你能夠這樣作:
---
- hosts: webservers
pre_tasks:
- shell: echo 'hello'
roles:
- { role: some_role }
tasks:
- shell: echo 'still busy'
post_tasks:
- shell: echo 'goodbye'
角色默認變量
要建立默認變量,只需在 roles 目錄下添加 defaults/main.yml 文件。這些變量在全部可用變量中擁有最低優先級,可能被其餘地方定義的變量(包括 inventory 中的變量)所覆蓋。
角色依賴
「角色依賴」 使你能夠自動地將其餘 roles 拉取到如今使用的 role 中。」角色依賴」 保存在 roles 目錄下的 meta/main.yml 文件中。這個文件應包含一列 roles 和 爲之指定的參數,下面是在 roles/myapp/meta/main.yml 文件中的示例:
---
dependencies:
- { role: common, some_parameter: 3 }
- { role: apache, port: 80 }
- { role: postgres, dbname: blarg, other_parameter: 12 }
「角色依賴」 也能夠經過源碼控制倉庫或者 tar 文件指定,使用逗號分隔:路徑、一個可選的版本(tag, commit, branch 等等)、一個可選友好角色名(嘗試從源碼倉庫名或者歸檔文件名中派生出角色名):
--- dependencies:
- { role: 'git+http://git.example.com/repos/role-foo,v1.1,foo' }
- { role: '/path/to/tar/file.tgz,,friendly-name' }
「角色依賴」 老是在 role (包含」角色依賴」的role)以前執行,而且是遞歸地執行。默認狀況下,做爲 「角色依賴」 被添加的 role 只能被添加一次,若是另外一個 role 將一個相同的角色列爲 「角色依賴」 的對象,它不會被重複執行。但這種默認的行爲可被修改,經過添加 allow_duplicates: yes 到meta/main.yml 文件中。 好比,一個 role 名爲 ‘car’,它能夠添加名爲 ‘wheel’ 的 role 到它的 「角色依賴」 中:
---
dependencies:
- { role: wheel, n: 1 }
- { role: wheel, n: 2 }
- { role: wheel, n: 3 }
- { role: wheel, n: 4 }
wheel 角色的 meta/main.yml 文件包含以下內容:
---
allow_duplicates: yes
dependencies:
- { role: tire }
- { role: brake }
最終的執行順序是這樣的:
tire(n=1)
brake(n=1)
wheel(n=1)
tire(n=2)
brake(n=2)
wheel(n=2)
...
car
YAML語法要求若是值以{{ foo }}開頭的話咱們須要將整行用雙引號包起來.這是爲了確認你不是想聲明一個YAML字典
這樣是不行的:
- hosts: app_servers
vars:
app_path: {{ base_path }}/22
你應該這麼作:
- hosts: app_servers
vars:
app_path: "{{ base_path }}/22"
Facts經過訪問遠程系統獲取相應的信息. 一個例子就是遠程主機的IP地址或者操做系統是什麼. 使用如下命令能夠查看哪些信息是可用的:[查看主機信息]
若是你不須要使用你主機的任何fact數據,你已經知道了你係統的一切,那麼你能夠關閉fact數據的獲取.這有利於加強Ansilbe面對大量系統的push模塊,或者你在實驗性平臺中使用Ansible.在任何playbook中能夠這樣作:[關閉facts]
- hosts: whatever
gather_facts: no
獲取主機名 {{ ansible_nodename }}
ansible hostname -m setup
命令輸出以下:
"ansible_all_ipv4_addresses": ["REDACTED IP ADDRESS"],
"ansible_all_ipv6_addresses": ["REDACTED IPV6 ADDRESS"], "ansible_architecture": "x86_64", "ansible_bios_date": "09/20/2012", "ansible_bios_version": "6.00", "ansible_cmdline": { "BOOT_IMAGE": "/boot/vmlinuz-3.5.0-23-generic", "quiet": true, "ro": true, "root": "UUID=4195bff4-e157-4e41-8701-e93f0aec9e22", "splash": true }, "ansible_date_time": { "date": "2013-10-02", "day": "02", "epoch": "1380756810", "hour": "19", "iso8601": "2013-10-02T23:33:30Z", "iso8601_micro": "2013-10-02T23:33:30.036070Z", "minute": "33", "month": "10", "second": "30", "time": "19:33:30", "tz": "EDT", "year": "2013" }, "ansible_default_ipv4": { "address": "REDACTED", "alias": "eth0", "gateway": "REDACTED", "interface": "eth0", "macaddress": "REDACTED", "mtu": 1500, "netmask": "255.255.255.0", "network": "REDACTED", "type": "ether" }, "ansible_default_ipv6": { }, "ansible_devices": { "fd0": { "holders": [], "host": "", "model": null, "partitions": { }, "removable": "1", "rotational": "1", "scheduler_mode": "deadline", "sectors": "0", "sectorsize": "512", "size": "0.00 Bytes", "support_discard": "0", "vendor": null }, "sda": { "holders": [], "host": "SCSI storage controller: LSI Logic / Symbios Logic 53c1030 PCI-X Fusion-MPT Dual Ultra320 SCSI (rev 01)", "model": "VMware Virtual S", "partitions": { "sda1": { "sectors": "39843840", "sectorsize": 512, "size": "19.00 GB", "start": "2048" }, "sda2": { "sectors": "2", "sectorsize": 512, "size": "1.00 KB", "start": "39847934" }, "sda5": { "sectors": "2093056", "sectorsize": 512, "size": "1022.00 MB", "start": "39847936" } }, "removable": "0", "rotational": "1", "scheduler_mode": "deadline", "sectors": "41943040", "sectorsize": "512", "size": "20.00 GB", "support_discard": "0", "vendor": "VMware," }, "sr0": { "holders": [], "host": "IDE interface: Intel Corporation 82371AB/EB/MB PIIX4 IDE (rev 01)", "model": "VMware IDE CDR10", "partitions": { }, "removable": "1", "rotational": "1", "scheduler_mode": "deadline", "sectors": "2097151", "sectorsize": "512", "size": "1024.00 MB", "support_discard": "0", "vendor": "NECVMWar" } }, "ansible_distribution": "Ubuntu", "ansible_distribution_release": "precise", "ansible_distribution_version": "12.04", "ansible_domain": "", "ansible_env": { "COLORTERM": "gnome-terminal", "DISPLAY": ":0", "HOME": "/home/mdehaan", "LANG": "C", "LESSCLOSE": "/usr/bin/lesspipe %s %s", "LESSOPEN": "| /usr/bin/lesspipe %s", "LOGNAME": "root", "LS_COLORS": "rs=0:di=01;34:ln=01;36:mh=00:pi=40;33:so=01;35:do=01;35:bd=40;33;01:cd=40;33;01:or=40;31;01:su=37;41:sg=30;43:ca=30;41:tw=30;42:ow=34;42:st=37;44:ex=01;32:*.tar=01;31:*.tgz=01;31:*.arj=01;31:*.taz=01;31:*.lzh=01;31:*.lzma=01;31:*.tlz=01;31:*.txz=01;31:*.zip=01;31:*.z=01;31:*.Z=01;31:*.dz=01;31:*.gz=01;31:*.lz=01;31:*.xz=01;31:*.bz2=01;31:*.bz=01;31:*.tbz=01;31:*.tbz2=01;31:*.tz=01;31:*.deb=01;31:*.rpm=01;31:*.jar=01;31:*.war=01;31:*.ear=01;31:*.sar=01;31:*.rar=01;31:*.ace=01;31:*.zoo=01;31:*.cpio=01;31:*.7z=01;31:*.rz=01;31:*.jpg=01;35:*.jpeg=01;35:*.gif=01;35:*.bmp=01;35:*.pbm=01;35:*.pgm=01;35:*.ppm=01;35:*.tga=01;35:*.xbm=01;35:*.xpm=01;35:*.tif=01;35:*.tiff=01;35:*.png=01;35:*.svg=01;35:*.svgz=01;35:*.mng=01;35:*.pcx=01;35:*.mov=01;35:*.mpg=01;35:*.mpeg=01;35:*.m2v=01;35:*.mkv=01;35:*.webm=01;35:*.ogm=01;35:*.mp4=01;35:*.m4v=01;35:*.mp4v=01;35:*.vob=01;35:*.qt=01;35:*.nuv=01;35:*.wmv=01;35:*.asf=01;35:*.rm=01;35:*.rmvb=01;35:*.flc=01;35:*.avi=01;35:*.fli=01;35:*.flv=01;35:*.gl=01;35:*.dl=01;35:*.xcf=01;35:*.xwd=01;35:*.yuv=01;35:*.cgm=01;35:*.emf=01;35:*.axv=01;35:*.anx=01;35:*.ogv=01;35:*.ogx=01;35:*.aac=00;36:*.au=00;36:*.flac=00;36:*.mid=00;36:*.midi=00;36:*.mka=00;36:*.mp3=00;36:*.mpc=00;36:*.ogg=00;36:*.ra=00;36:*.wav=00;36:*.axa=00;36:*.oga=00;36:*.spx=00;36:*.xspf=00;36:", "MAIL": "/var/mail/root", "OLDPWD": "/root/ansible/docsite", "PATH": "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", "PWD": "/root/ansible", "SHELL": "/bin/bash", "SHLVL": "1", "SUDO_COMMAND": "/bin/bash", "SUDO_GID": "1000", "SUDO_UID": "1000", "SUDO_USER": "mdehaan", "TERM": "xterm", "USER": "root", "USERNAME": "root", "XAUTHORITY": "/home/mdehaan/.Xauthority", "_": "/usr/local/bin/ansible" }, "ansible_eth0": { "active": true, "device": "eth0", "ipv4": { "address": "REDACTED", "netmask": "255.255.255.0", "network": "REDACTED" }, "ipv6": [{ "address": "REDACTED", "prefix": "64", "scope": "link" }], "macaddress": "REDACTED", "module": "e1000", "mtu": 1500, "type": "ether" }, "ansible_form_factor": "Other", "ansible_fqdn": "ubuntu2.example.com", "ansible_hostname": "ubuntu2", "ansible_interfaces": ["lo", "eth0"], "ansible_kernel": "3.5.0-23-generic", "ansible_lo": { "active": true, "device": "lo", "ipv4": { "address": "127.0.0.1", "netmask": "255.0.0.0", "network": "127.0.0.0" }, "ipv6": [{ "address": "::1", "prefix": "128", "scope": "host" }], "mtu": 16436, "type": "loopback" }, "ansible_lsb": { "codename": "precise", "description": "Ubuntu 12.04.2 LTS", "id": "Ubuntu", "major_release": "12", "release": "12.04" }, "ansible_machine": "x86_64", "ansible_memfree_mb": 74, "ansible_memtotal_mb": 991, "ansible_mounts": [{ "device": "/dev/sda1", "fstype": "ext4", "mount": "/", "options": "rw,errors=remount-ro", "size_available": 15032406016, "size_total": 20079898624 }], "ansible_nodename": "ubuntu2.example.com", "ansible_os_family": "Debian", "ansible_pkg_mgr": "apt", "ansible_processor": ["Intel(R) Core(TM) i7 CPU 860 @ 2.80GHz"], "ansible_processor_cores": 1, "ansible_processor_count": 1, "ansible_processor_threads_per_core": 1, "ansible_processor_vcpus": 1, "ansible_product_name": "VMware Virtual Platform", "ansible_product_serial": "REDACTED", "ansible_product_uuid": "REDACTED", "ansible_product_version": "None", "ansible_python_version": "2.7.3", "ansible_selinux": false, "ansible_ssh_host_key_dsa_public": "REDACTED KEY VALUE""ansible_ssh_host_key_ecdsa_public": "REDACTED KEY VALUE""ansible_ssh_host_key_rsa_public": "REDACTED KEY VALUE""ansible_swapfree_mb": 665, "ansible_swaptotal_mb": 1021, "ansible_system": "Linux", "ansible_system_vendor": "VMware, Inc.", "ansible_user_id": "root", "ansible_userspace_architecture": "x86_64", "ansible_userspace_bits": "64", "ansible_virtualization_role": "guest", "ansible_virtualization_type": "VMware"
本地Facts
若是遠程受管理的機器有一個 「/etc/ansible/facts.d」 目錄,那麼在該目錄中任何以 」.fact」結尾的文件均可以在Ansible中提供局部facts.這些文件能夠是JSON,INI或者任何能夠返回JSON的可執行文件.
例如建設有一個 /etc/ansible/facts.d/perferences.fact文件:
[general]
asdf=1
bar=2
這將產生一個名爲 「general」 的哈希表fact,裏面成員有 ‘asdf’ 和 ‘bar’. 能夠這樣驗證:
ansible <hostname> -m setup -a "filter=ansible_local"
而後你會看到有如下fact被添加:
"ansible_local": {
"preferences": {
"general": {
"asdf" : "1",
"bar" : "2" } } }
並且也能夠在template或palybook中訪問該數據:
{{ ansible_local.preferences.general.asdf }}
本地命名空間放置其它用戶提供的fact或者playbook中定義的變量覆蓋系統facts值.
若是你有個一個playook,它複製了一個自定義的fact,而後運行它,請顯式調用來從新運行setup模塊,這樣可讓咱們在該playbook中使用這些fact.不然,在下一個play中才能獲取這些自定義的fact信息.這裏有一個示例:
- hosts: webservers
tasks:
- name: create directory for ansible custom facts
file: state=directory recurse=yes path=/etc/ansible/facts.d
- name: install custom impi fact
copy: src=ipmi.fact dest=/etc/ansible/facts.d
- name: re-read facts after adding custom fact
setup: filter=ansible_local
然而在該模式中你也能夠編寫一個fact模塊,這只不過是多了一個選項.
Fact緩存
從一個服務器引用另外一個服務器的變量是可行的
{{ hostvars['asdf.example.com']['ansible_os_family'] }}
註冊變量
register
- hosts: web_servers
tasks:
- shell: /usr/bin/foo
register: foo_result
ignore_errors: True
- shell: /usr/bin/bar
when: foo_result.rc == 5
魔法變量,以及如何訪問其它主機的信息
Ansible會自動提供給你一些變量,即便你並無定義過它們.這些變量中重要的有 ‘hostvars’,’group_names’,和 ‘groups’.因爲這些變量名是預留的,因此用戶不該當覆蓋它們. ‘environmen’ 也是預留的. hostvars可讓你訪問其它主機的變量,包括哪些主機中獲取到的facts.若是你尚未在當前playbook或者一組playbook的任何play中訪問那個主機,那麼你能夠獲取變量,但沒法看到facts值. 若是數據庫服務器想使用另外一個節點的某個 ‘fact’ 值,或者賦值給該節點的一個inventory變量.能夠在一個模板中甚至命令行中輕鬆實現:
{{ hostvars['test.example.com']['ansible_distribution'] }}
另外, group_names 是當前主機所在全部羣組的列表(數組).因此可使用Jinja2語法在模板中根據該主機所在羣組關係(或角色)來產生變化:
{% if 'webserver' in group_names %}
# some part of a configuration file that only applies to webservers
{% endif %}
groups 是inventory中全部羣組(主機)的列表.可用於枚舉羣組中的全部主機.例如:
{% for host in groups['app_servers'] %}
# something that applies to all app servers.
{% endfor %}
一個常用的範式是找出該羣組中的全部IP地址:
{% for host in groups['app_servers'] %}
{{ hostvars[host]['ansible_eth0']['ipv4']['address'] }}
{% endfor %}
外部的變量文件:
---
- hosts: all
remote_user: root
vars:
favcolor: blue
vars_files:
- /vars/external_vars.yml
tasks:
- name: this is just a placeholder
command: /bin/echo foo
這能夠保證你共享playbook源碼時隔離敏感數據的風險.
每一個變量文件的內容是一個簡單的YAML文件,以下所示:
---
# in the above example, this would be vars/external_vars.yml
somevar: somevalue
password: magic
變量的優先級,變量優先級
* extra vars (-e in the command line) always win
* then comes connection variables defined in inventory (ansible_ssh_user, etc)
* then comes "most everything else" (command line switches, vars in play, included vars, role vars, etc)
* then comes the rest of the variables defined in inventory
* then comes facts discovered about a system * then "role defaults", which are the most "defaulty" and lose in priority to everything.
* extra vars (在命令行中使用 -e)優先級最高
* 而後是在inventory中定義的鏈接變量(好比ansible_ssh_user)
* 接着是大多數的其它變量(命令行轉換,play中的變量,included的變量,role中的變量等)
* 而後是在inventory定義的其它變量
* 而後是由系統發現的facts
* 而後是 "role默認變量", 這個是最默認的值,很容易喪失優先權
when
- include: tasks/sometasks.yml
when: "'reticulating splines' in output"
或者應用於role:
- hosts: webservers
roles:
- { role: debian_stock_config, when: ansible_os_family == 'Debian' }
- name: add several users
user: name={{ item }} state=present groups=wheel
with_items:
- testuser1
- testuser2
或者
with_items: "{{somelist}}"
或者
- name: add several users
user: name={{ item.name }} state=present groups={{ item.groups }}
with_items:
- { name: 'testuser1', groups: 'wheel' }
- { name: 'testuser2', groups: 'root' }
- name: give users access to multiple databases
mysql_user: name={{ item[0] }} priv={{ item[1] }}.*:ALL append_privs=yes password=foo
with_nested:
- [ 'alice', 'bob' ]
- [ 'clientdb', 'employeedb', 'providerdb' ]
對哈希表使用循環
New in version 1.5.
假如你有如下變量:
--- users:
alice:
name: Alice Appleworth
telephone: 123-456-7890
bob:
name: Bob Bananarama
telephone: 987-654-3210
你想打印出每一個用戶的名稱和電話號碼.你可使用 with_dict 來循環哈希表中的元素:
tasks:
- name: Print phone records
debug: msg="User {{ item.key }} is {{ item.value.name }} ({{ item.value.telephone }})"
with_dict: "{{users}}"
對文件列表使用循環
with_fileglob 能夠以非遞歸的方式來模式匹配單個目錄中的文件.以下面所示:
---
- hosts: all
tasks:
# first ensure our target directory exists
- file: dest=/etc/fooapp state=directory
# copy each file over that matches the given pattern
- copy: src={{ item }} dest=/etc/fooapp/ owner=root mode=600
with_fileglob:
- /playbooks/files/fooapp/*
對子元素使用循環
假設你想對一組用戶作一些動做,好比建立這些用戶,而且容許它們使用一組SSH key來登陸.
如何實現那? 先假設你有按如下方式定義的數據,能夠經過」vars_files」或」group_vars/all」文件加載:
--- users:
- name: alice
authorized:
- /tmp/alice/onekey.pub
- /tmp/alice/twokey.pub
mysql:
password: mysql-password
hosts:
- "%"
- "127.0.0.1"
- "::1"
- "localhost"
privs:
- "*.*:SELECT"
- "DB1.*:ALL"
- name: bob
authorized:
- /tmp/bob/id_rsa.pub
mysql:
password: other-mysql-password
hosts:
- "db1"
privs:
- "*.*:SELECT"
- "DB2.*:ALL"
那麼能夠這樣實現:
- user: name={{ item.name }} state=present generate_ssh_key=yes
with_items: "{{users}}"
- authorized_key: "user={{ item.0.name }} key='{{ lookup('file', item.1) }}'"
with_subelements:
- users
- authorized
根據mysql hosts以及預先給定的privs subkey列表,咱們也能夠在嵌套的subkey中迭代列表:
- name: Setup MySQL users
mysql_user: name={{ item.0.user }} password={{ item.0.mysql.password }} host={{ item.1 }} priv={{ item.0.mysql.privs | join('/') }}
with_subelements:
- users
- mysql.hosts Subelements walks a list of hashes (aka dictionaries) and then traverses a list with a given key inside of those records.
你也能夠爲字元素列表添加第三個元素,該元素能夠放置標誌位字典.如今你能夠加入’skip_missing’標誌位.若是設置爲True,那麼查找插件會跳過不包含指定子鍵的列表條目.若是沒有該標誌位,或者標誌位值爲False,插件會產生錯誤並指出缺乏該子鍵.
這就是authorized_key模式中key的獲取方式.
對整數序列使用循環
with_sequence 能夠以升序數字順序生成一組序列.你能夠指定起始值、終止值,以及一個可選的步長值.
指定參數時也可使用key=value這種鍵值對的方式.若是採用這種方式,’format’是一個可打印的字符串.
數字值能夠被指定爲10進制,16進制(0x3f8)或者八進制(0600).負數則不受支持.請看如下示例:
---
- hosts: all
tasks: # create groups
- group: name=evens state=present
- group: name=odds state=present
# create some test users
- user: name={{ item }} state=present groups=evens
with_sequence: start=0 end=32 format=testuser%02x
# create a series of directories with even numbers for some reason
-file: dest=/var/stuff/{{ item }} state=directory
with_sequence: start=4 end=16 stride=2
# a simpler way to use the sequence plugin
# create 4 groups
- group: name=group{{ item }} state=present
with_sequence: count=4
隨機選擇
‘random_choice’功能能夠用來隨機獲取一些值.它並非負載均衡器(已經有相關的模塊了).它有時能夠用做一個簡化版的負載均衡器,好比做爲條件判斷:
- debug: msg={{ item }}
with_random_choice:
- "go through the door"
- "drink from the goblet"
- "press the red button"
- "do nothing"
有時你想重試一個任務直到達到某個條件.好比下面這個例子:[重複執行]
- action: shell /usr/bin/foo
register: result
until: result.stdout.find("all systems go") != -1
retries: 5
delay: 10
上面的例子遞歸運行shell模塊,直到模塊結果中的stdout輸出中包含」all systems go」字符串,或者該任務按照10秒的延遲重試超過5次.」retries」和」delay」的默認值分別是3和5.
該任務返回最後一個任務返回的結果.單次重試的結果可使用-vv選項來查看. 被註冊的變量會有一個新的屬性’attempts’,值爲該任務重試的次數.
查找第一個匹配的文件
這其實不是一個循環,但和循環很類似.若是你想引用一個文件,而該文件是從一組文件中根據給定條件匹配出來的.這組文件中部分文件名由變量拼接而成.針對該場景你能夠這樣作:[動態文件名]
- name: INTERFACES | Create Ansible header for /etc/network/interfaces
template: src={{ item }} dest=/etc/foo.conf
with_first_found:
- "{{ansible_virtualization_type}}_foo.conf"
- "default_foo.conf"
該功能還有一個更完整的版本,能夠配置搜索路徑.請看如下示例:
- name: some configuration template
template: src={{ item }} dest=/etc/file.cfg mode=0444 owner=root group=root
with_first_found:
- files:
- "{{inventory_hostname}}/etc/file.cfg"
paths:
- ../../../templates.overwrites
- ../../../templates
- files:
- etc/file.cfg
paths:
- templates
異步操做和輪詢
默認狀況下playbook中的任務執行時會一直保持鏈接,直到該任務在每一個節點都執行完畢.有時這是沒必要要的,好比有些操做運行時間比SSH超時時間還要長.
解決該問題最簡單的方式是一塊兒執行它們,而後輪詢直到任務執行完畢,簡單的意思就是,像下面的例子,執行任務後,ansible就不等它了,往下執行下一個任務,而後每隔5秒鐘去看看它執行完成沒,超時時間爲45秒,async參數值表明了這個任務執行時間的上限值。即任務執行所用時間若是超出這個時間,則認爲任務失敗。此參數若未設置,則爲同步執行。
你也能夠對執行時間很是長(有可能遭遇超時)的操做使用異步模式.
爲了異步啓動一個任務,能夠指定其最大超時時間以及輪詢其狀態的頻率.若是你沒有爲 poll 指定值,那麼默認的輪詢頻率是10秒鐘:
---
- hosts: all
remote_user: root
tasks:
- name: simulate long running op (15 sec), wait for up to 45 sec, poll every 5 sec
command: /bin/sleep 15
async: 45
poll: 5
async 並無默認值,若是你沒有指定 async 關鍵字,那麼任務會以同步的方式運行,這是Ansible的默認行爲.
另外,若是你不須要等待任務執行完畢,你能夠指定 poll 值爲0而啓用 「啓動並忽略」
---
- hosts: all
remote_user: root
tasks:
- name: simulate long running op, allow to run for 45 sec, fire and forget
command: /bin/sleep 15
async: 45
poll: 0
改成」啓動並忽略,稍後再檢查」,你可使用如下方式執行任務:
---
# Requires ansible 1.8+
- name: 'YUM - fire and forget task'
yum: name=docker-io state=installed
async: 1000
poll: 0
register: yum_sleeper
- name: 'YUM - check on fire and forget task'
async_status: jid={{ yum_sleeper.ansible_job_id }}
register: job_result
until: job_result.finished
retries: 30
若是 async: 值過小,可能會致使 「稍後檢查」 任務執行失敗,由於 async_status:: 的臨時狀態文件還未被寫入信息,而」稍後檢查」任務就試圖讀取此文件.
delegate_to 選中主機執行
- name: add back to load balancer pool
command: /usr/bin/add_back_to_pool {{ inventory_hostname }}
delegate_to: 127.0.0.1
本地執行
---
# ...
tasks:
- name: recursively copy files from management server to target
local_action: command rsync -a /path/to/files {{ inventory_hostname }}:/path/to/target/
Run Once
New in version 1.7.
有時候你有這樣的需求,在一個主機上面只執行一次一個任務.這樣的配置能夠配置」run_once」來實現:
---
# ...
tasks:
# ...
- command: /opt/application/upgrade_db.py
run_once: true # ...
這樣能夠添加在」delegat_to」選項對中來定義要執行的主機:
- command: /opt/application/upgrade_db.py
run_once: true
delegate_to: web01.example.org
當」run_once」 沒有喝」delegate_to」一塊兒使用,這個任務將會被清單指定的第一個主機. 在一組被play制定主機.例如 webservers[0], 若是play指定爲 「hosts: webservers」.
這個方法也很相似,雖然比使用條件更加簡單粗暴,以下事例:
- command: /opt/application/upgrade_db.py
when: inventory_hostname == webservers[0]
本地Playbooks
在本地使用playbook有時候比ssh遠程使用更加有用.能夠經過把playbook放在crontab中,來確保一個系統的配置,能夠頗有用. 在OS installer 中運行一個playbook也頗有用.例如Anaconda kickstart.
要想在本地運行一個play,能夠直接設置」host:」 與 「hosts:127.0.0.1」, 而後使用下面的命令運行:
ansible-playbook playbook.yml --connection=local
或者,一個本地鏈接也能夠做爲一個單獨的playbook play應用在playbook中, 即使playbook中其餘的plays使用默認遠程 鏈接以下:
- hosts: 127.0.0.1
connection: local
environment 使用代理上網
- hosts: all
remote_user: root
tasks:
- apt: name=cobbler state=installed
environment:
http_proxy: http://proxy.example.com:8080
environment 也能夠被存儲在變量中,像以下方式訪問:
- hosts: all
remote_user: root
# here we make a variable named "proxy_env" that is a dictionary
vars:
proxy_env:
http_proxy: http://proxy.example.com:8080
tasks:
- apt: name=cobbler state=installed
environment: proxy_env
指定錯誤條件
[斷定錯誤]
- name: this command prints FAILED when it fails
command: /usr/bin/example-command -x -y -z
register: command_result
failed_when: "'FAILED' in command_result.stderr"
在 Ansible 1.4 以前的版本能經過以下方式完成:
- name: this command prints FAILED when it fails
command: /usr/bin/example-command -x -y -z
register: command_result ignore_errors: True
- name: fail the play if the previous command did not succeed
fail: msg="the command failed"
when: "'FAILED' in command_result.stderr
不輸出結果,不報告狀態,覆寫結果
tasks:
- shell: /usr/bin/billybass --mode="take me to the river"
register: bass_result
changed_when: "bass_result.rc != 2"
# this will never report 'changed' status
- shell: wall 'beep'
changed_when: False
標籤tags
若是你有一個大型的 playbook,那可以只運行其中特定部分的配置而無需運行整個 playbook 將會頗有用.
plays 和 tasks 都因這個理由而支持 「tags:」
例:
tasks:
- yum: name={{ item }} state=installed
with_items:
- httpd
- memcached
tags:
- packages
- template: src=templates/src.j2 dest=/etc/foo.conf
tags:
- configuration
若是你只想運行一個很是大的 playbook 中的 「configuration」 和 「packages」,你能夠這樣作:
ansible-playbook example.yml --tags "configuration,packages"
另外一方面,若是你只想執行 playbook 中某個特定任務 以外 的全部任務,你能夠這樣作:
ansible-playbook example.yml --skip-tags "notification"
你一樣也能夠對 roles 應用 tags:
roles:
- { role: webserver, port: 5000, tags: [ 'web', 'foo' ] }
你一樣也能夠對基本的 include 語句使用 tag:
- include: foo.yml tags=web,foo
從指定任務開始運行palybook以及分步運行playbook
如下列出了幾種方式來運行playbook.這對於測試或調試新的playbook頗有幫助.
Start-at-task
若是你想從指定的任務開始執行playbook,可使用``–start-at``選項:
ansible-playbook playbook.yml --start-at="install packages"
以上命令就會在名爲」install packages」的任務開始執行你的playbook.
分步運行playbook
咱們也能夠經過``–step``選項來交互式的執行playbook:
ansible-playbook playbook.yml --step
這樣ansible在每一個任務前會自動中止,並詢問是否應該執行該任務.
好比你有個名爲``configure ssh``的任務,playbook執行到這裏會中止並詢問:
Perform task: configure ssh (y/n/c):
「y」回答會執行該任務,」n」回答會跳過該任務,而」c」回答則會繼續執行剩餘的全部任務而再也不詢問你.
python ansible api調用
#!/usr/bin/env python2 import sys import json import shutil import pprint from collections import namedtuple from ansible.parsing.dataloader import DataLoader from ansible.vars.manager import VariableManager from ansible.inventory.manager import InventoryManager from ansible.playbook.play import Play from ansible.executor.task_queue_manager import TaskQueueManager from ansible.plugins.callback import CallbackBase import ansible.constants as C from ansible.inventory.group import Group from ansible.inventory.host import Host def get_info(username,password,resource): class ResultCallback(CallbackBase): def __init__(self, *args, **kwargs): self.info = {} def v2_runner_on_ok(self, result, **kwargs): host = result._host self.info[host.name] = result._result Options = namedtuple('Options', ['connection', 'module_path', 'forks', 'become', 'become_method', 'become_user', 'check', 'diff']) options = Options(connection='local', module_path=['/to/mymodules'], forks=10, become=None, become_method=None, become_user=None, check=False, diff=False) loader = DataLoader() results_callback = ResultCallback() #inventory = InventoryManager(loader=loader, sources=[tempFileName]) inventory = InventoryManager(loader=loader) inventory.add_group("default") for host in resource: inventory.add_host(host=host,port=22,group='default') variable_manager = VariableManager(loader=loader, inventory=inventory) for host in resource: host = inventory.get_host(hostname=host) variable_manager.set_host_variable(host=host,varname='ansible_ssh_host',value=host) variable_manager.set_host_variable(host=host,varname='ansible_ssh_user',value=username) variable_manager.set_host_variable(host=host,varname='ansible_ssh_pass',value=password) variable_manager.set_host_variable(host=host,varname='ansible_connection',value='local') play_source = dict( name = "Ansible Play", hosts = resource, gather_facts = 'no', tasks = [ dict(action=dict(module='setup', args='')) ] ) play = Play().load(play_source, variable_manager=variable_manager, loader=loader) tqm = None try: tqm = TaskQueueManager( inventory=inventory, variable_manager=variable_manager, loader=loader, options=options, passwords=None, stdout_callback=results_callback, ) result = tqm.run(play) finally: if tqm is not None: tqm.cleanup() shutil.rmtree(C.DEFAULT_LOCAL_TMP, True) return results_callback.info def handle_info(rawdata, node): # data dictionary data = {} detail = {} # read sysinfo cpu_count = rawdata[node]['ansible_facts']['ansible_processor_count'] cpu_cores = rawdata[node]['ansible_facts']['ansible_processor_cores'] cpu_model = list(set(rawdata[node]['ansible_facts']['ansible_processor'][2::3])) cpu_vcpus = rawdata[node]['ansible_facts']['ansible_processor_vcpus'] # memtotal = rawdata[node]['ansible_facts']['ansible_memtotal_mb'] # memfree = rawdata[node]['ansible_facts']['ansible_memfree_mb'] memory = rawdata[node]['ansible_facts']['ansible_memory_mb']['real'] disk_info = rawdata[node]['ansible_facts']['ansible_devices'] interfaces_info = rawdata[node]['ansible_facts']['ansible_interfaces'] for i in range(len(interfaces_info)): tmp = "ansible_" + interfaces_info[i].replace("-", "_") if rawdata[node]['ansible_facts'][tmp].has_key('type'): if rawdata[node]['ansible_facts'][tmp]['type'] == "bridge": continue detail[interfaces_info[i]] = {} detail[interfaces_info[i]]['active'] = rawdata[node]['ansible_facts'][tmp]['active'] detail[interfaces_info[i]]['mtu'] = rawdata[node]['ansible_facts'][tmp]['mtu'] detail[interfaces_info[i]]['promisc'] = rawdata[node]['ansible_facts'][tmp]['promisc'] if rawdata[node]['ansible_facts'][tmp].has_key('speed'): detail[interfaces_info[i]]['speed'] = rawdata[node]['ansible_facts'][tmp]['speed'] if rawdata[node]['ansible_facts'][tmp].has_key('type'): detail[interfaces_info[i]]['type'] = rawdata[node]['ansible_facts'][tmp]['type'] # store sysinfo into data dictionary cpu = {} cpu['cpu_number'] = cpu_count cpu['cpu_cores'] = cpu_cores cpu['cpu_model'] = cpu_model cpu['cpu_vcpus'] = cpu_vcpus data['cpu'] = cpu data['memory'] = memory disk = {} disk['number'] = len(disk_info) disk['info'] = {} for key in disk_info: disk['info'][key] = {} disk['info'][key]['size'] = disk_info[key]['size'] data['disk'] = disk interfaces = {} interfaces['number'] = len(interfaces_info) interfaces['info'] = detail data['interfaces'] = interfaces return data def handle_infos(rawdata): info = [] for key in rawdata: temp = {} temp['IP_ADDR'] = key temp['OBJ_ATTRS'] = handle_info(rawdata, key) info.append(temp) return info if __name__ == '__main__': if len(sys.argv) != 4: print "need parameter:username,password,list of hosts(split with ',')!!!" sys.exit() hosts=sys.argv[3].split(",") raw_data = get_info(sys.argv[1],sys.argv[2],hosts) data = handle_infos(raw_data) data = json.dumps(data) pprint.pprint(data)
晚安( ̄o ̄) . z Z