自動化運維中的萬人迷-ansible

自動化運維中的萬人迷-ansible

標誌

一.簡介

設計原則:

有一個固定的簡單的設定過程和最小的學習曲線

預設情況下超快速並行

不需要伺服器或客戶端守護程序; 使用現有的sshd

關注安全性且易於審計/審查/重寫內容

即時管理遠端機器,無需自舉

允許使用任何動態語言進行模組開發,而不僅僅是Python

可用作非root使用者

成為最容易使用的IT自動化系統

二、安裝

ansible依賴於Python 2。6或更高的版本、paramiko、PyYAML及Jinja2。

2。1 編譯安裝

解決依賴關係

# yum -y install python-jinja2 PyYAML python-paramiko python-babel python-crypto

# tar xf ansible-1。5。4。tar。gz

# cd ansible-1。5。4

# python setup。py build

# python setup。py install

# mkdir /etc/ansible

# cp -r examples/* /etc/ansible

2。2 rpm包安裝

# yum install ansible

注意:不同版本的ansible的功能差異可能較大。

三、簡單應用

ansible透過ssh實現配置管理、應用部署、任務執行等功能,因此,需要事先配置ansible端能基於金鑰認證的方式聯絡各被管理節點。

ansible [-f forks] [-m module_name] [-a args]

-m module:預設為command

ansible-doc: Show Ansible module documentation

-l, ——list List available modules

-s, ——snippet Show playbook snippet for specified module(s)

四、YAML

4。1 YAML介紹

YAML是一個可讀性高的用來表達資料序列的格式。YAML參考了其他多種語言,包括:XML、C語言、Python、Perl以及電子郵件格式RFC2822等。Clark Evans在2001年在首次發表了這種語言,另外Ingy döt Net與Oren Ben-Kiki也是這語言的共同設計者。

YAML Ain‘t Markup Language,即YAML不是XML。不過,在開發的這種語言時,YAML的意思其實是:“Yet Another Markup Language”(仍是一種標記語言)。其特性:

YAML的可讀性好

YAML和指令碼語言的互動性好

YAML使用實現語言的資料型別

YAML有一個一致的資訊模型

YAML易於實現

YAML可以基於流來處理

YAML表達能力強,擴充套件性好

更多的內容及規範參見http://www。yaml。org。

4。2 YAML語法

YAML的語法和其他高階語言類似,並且可以簡單表達清單、散列表、標量等資料結構。其結構(Structure)透過空格來展示,序列(Sequence)裡的項用“-”來代表,Map裡的鍵值對用“:”分隔。下面是一個示例。

name: John Smith

age: 41

gender: Male

spouse:

name: Jane Smith

age: 37

gender: Female

children:

- name: Jimmy Smith

age: 17

gender: Male

- name: Jenny Smith

age 13

gender: Female

YAML副檔名通常為。yaml,如example。yaml。

4。2。1 list

列表的所有元素均使用“-”打頭,例如:

# A list of tasty fruits

- Apple

- Orange

- Strawberry

- Mango

4。2。2 dictionary

字典透過key與value進行標識,例如:

——-

# An employee record

name: Example Developer

job: Developer

skill: Elite

也可以將key:value放置於{}中進行表示,例如:

——-

# An employee record

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

五、Ansible基礎元素

5。1 變數

5。1。1 變數命名

變數名僅能由字母、數字和下劃線組成,且只能以字母開頭。

5。1。2 facts

facts是由正在通訊的遠端目標主機發回的資訊,這些資訊被儲存在ansible變數中。要獲取指定的遠端主機所支援的所有facts,可使用如下命令進行:

# ansible hostname -m setup

5。1。3 register

把任務的輸出定義為變數,然後用於其他任務,示例如下:

tasks:

- shell: /usr/bin/foo

register: foo_result

ignore_errors: True

5。1。4 透過命令列傳遞變數

在執行playbook的時候也可以傳遞一些變數供playbook使用,示例如下:

ansible-playbook test。yml ——extra-vars “hosts=www user=mageedu”

5。1。5 透過roles傳遞變數

當給一個主機應用角色的時候可以傳遞變數,然後在角色內使用這些變數,示例如下:

- hosts: webservers

roles:

- common

- { role: foo_app_instance, dir: ’/web/htdocs/a。com‘, port: 8080 }

5。2 Inventory

ansible的主要功用在於批次主機操作,為了便捷地使用其中的部分主機,可以在inventory file中將其分組命名。預設的inventory file為/etc/ansible/hosts。

inventory file可以有多個,且也可以透過Dynamic Inventory來動態生成。

5。2。1 inventory檔案格式

inventory檔案遵循INI檔案風格,中括號中的字元為組名。可以將同一個主機同時歸併到多個不同的組中;此外,當如若目標主機使用了非預設的SSH埠,還可以在主機名稱之後使用冒號加埠號來標明。

ntp。magedu。com

[webservers]

www1。magedu。com:2222

www2。magedu。com

[dbservers]

db1。magedu。com

db2。magedu。com

db3。magedu。com

如果主機名稱遵循相似的命名模式,還可以使用列表的方式標識各主機,例如:

[webservers]

www[01:50]。example。com

[databases]

db-[a:f]。example。com

5。2。2 主機變數

可以在inventory中定義主機時為其新增主機變數以便於在playbook中使用。例如:

[webservers]

www1。magedu。com http_port=80 maxRequestsPerChild=808

www2。magedu。com http_port=8080 maxRequestsPerChild=909

5。2。3 組變數

組變數是指賦予給指定組內所有主機上的在playboo中可用的變數。例如:

[webservers]

www1。magedu。com

www2。magedu。com

[webservers:vars]

ntp_server=ntp。magedu。com

nfs_server=nfs。magedu。com

5。2。4 組巢狀

inventory中,組還可以包含其它的組,並且也可以向組中的主機指定變數。不過,這些變數只能在ansible-playbook中使用,而ansible不支援。例如:

[apache]

httpd1。magedu。com

httpd2。magedu。com

[nginx]

ngx1。magedu。com

ngx2。magedu。com

[webservers:children]

apache

nginx

[webservers:vars]

ntp_server=ntp。magedu。com

5。2。5 inventory引數

ansible基於ssh連線inventory中指定的遠端主機時,還可以透過引數指定其互動方式;這些引數如下所示:

ansible_ssh_host

The name of the host to connect to, if different from the alias you wish to give to it。

ansible_ssh_port

The ssh port number, if not 22

ansible_ssh_user

The default ssh user name to use。

ansible_ssh_pass

The ssh password to use (this is insecure, we strongly recommend using ——ask-pass or SSH keys)

ansible_sudo_pass

The sudo password to use (this is insecure, we strongly recommend using ——ask-sudo-pass)

ansible_connection

Connection type of the host。 Candidates are local, ssh or paramiko。 The default is paramiko before Ansible 1。2, and ’smart‘ afterwards which detects whether usage of ’ssh‘ would be feasible based on whether ControlPersist is supported。

ansible_ssh_private_key_file

Private key file used by ssh。 Useful if using multiple keys and you don’t want to use SSH agent。

ansible_shell_type

The shell type of the target system。 By default commands are formatted using ‘sh’-style syntax by default。 Setting this to ‘csh’ or ‘fish’ will cause commands executed on target systems to follow those shell‘s syntax instead。

ansible_python_interpreter

The target host python path。 This is useful for systems with more

than one Python or not located at “/usr/bin/python” such as \*BSD, or where /usr/bin/python

is not a 2。X series Python。 We do not use the “/usr/bin/env” mechanism as that requires the remote user’s

path to be set right and also assumes the “python” executable is named python, where the executable might

be named something like “python26”。

ansible\_\*\_interpreter

Works for anything such as ruby or perl and works just like ansible_python_interpreter。

This replaces shebang of modules which will run on that host。

5。3 條件測試

如果需要根據變數、facts或此前任務的執行結果來做為某task執行與否的前提時要用到條件測試。

5。3。1 when語句

在task後新增when子句即可使用條件測試;when語句支援Jinja2表示式語法。例如:

tasks:

- name: “shutdown Debian flavored systems”

command: /sbin/shutdown -h now

when: ansible_os_family == “Debian”

when語句中還可以使用Jinja2的大多“filter”,例如要忽略此前某語句的錯誤並基於其結果(failed或者sucess)執行後面指定的語句,可使用類似如下形式:

tasks:

- command: /bin/false

register: result

ignore_errors: True

- command: /bin/something

when: result|failed

- command: /bin/something_else

when: result|success

- command: /bin/still/something_else

when: result|skipped

此外,when語句中還可以使用facts或playbook中定義的變數。

5。4 迭代

當有需要重複性執行的任務時,可以使用迭代機制。其使用格式為將需要迭代的內容定義為item變數引用,並透過with_items語句來指明迭代的元素列表即可。例如:

- name: add several users

user: name={{ item }} state=present groups=wheel

with_items:

- testuser1

- testuser2

上面語句的功能等同於下面的語句:

- name: add user testuser1

user: name=testuser1 state=present groups=wheel

- name: add user testuser2

user: name=testuser2 state=present groups=wheel

事實上,with_items中可以使用元素還可為hashes,例如:

- name: add several users

user: name={{ item。name }} state=present groups={{ item。groups }}

with_items:

- { name: ‘testuser1’, groups: ‘wheel’ }

- { name: ‘testuser2’, groups: ‘root’ }

ansible的迴圈機制還有更多的高階功能,具體請參見官方文件(http://docs。ansible。com/playbooks_loops。html)。

六、ansible playbooks

playbook是由一個或多個“play”組成的列表。play的主要功能在於將事先歸併為一組的主機裝扮成事先透過ansible中的task定義好的角色。從根本上來講,所謂task無非是呼叫ansible的一個module。將多個play組織在一個playbook中,即可以讓它們聯同起來按事先編排的機制同唱一臺大戲。下面是一個簡單示例。

- hosts: webnodes

vars:

http_port: 80

max_clients: 256

remote_user: root

tasks:

- name: ensure apache is at the latest version

yum: name=httpd state=latest

- name: ensure apache is running

service: name=httpd state=started

handlers:

- name: restart apache

service: name=httpd state=restarted

6。1 playbook基礎元件

6。1。1 Hosts和Users

playbook中的每一個play的目的都是為了讓某個或某些主機以某個指定的使用者身份執行任務。hosts用於指定要執行指定任務的主機,其可以是一個或多個由冒號分隔主機組;remote_user則用於指定遠端主機上的執行任務的使用者。如上面示例中的

-hosts: webnodes

remote_user: root

不過,remote_user也可用於各task中。也可以透過指定其透過sudo的方式在遠端主機上執行任務,其可用於play全域性或某任務;此外,甚至可以在sudo時使用sudo_user指定sudo時切換的使用者。

- hosts: webnodes

remote_user: mageedu

tasks:

- name: test connection

ping:

remote_user: mageedu

sudo: yes

6。1。2 任務列表和action

play的主體部分是task list。task list中的各任務按次序逐個在hosts中指定的所有主機上執行,即在所有主機上完成第一個任務後再開始第二個。在執行自下而下某playbook時,如果中途發生錯誤,所有已執行任務都將回滾,因此,在更正playbook後重新執行一次即可。

task的目的是使用指定的引數執行模組,而在模組引數中可以使用變數。模組執行是冪等的,這意味著多次執行是安全的,因為其結果均一致。

每個task都應該有其name,用於playbook的執行結果輸出,建議其內容儘可能清晰地描述任務執行步驟。如果未提供name,則action的結果將用於輸出。

定義task的可以使用“action: module options”或“module: options”的格式,推薦使用後者以實現向後相容。如果action一行的內容過多,也中使用在行首使用幾個空白字元進行換行。

tasks:

- name: make sure apache is running

service: name=httpd state=running

在眾多模組中,只有command和shell模組僅需要給定一個列表而無需使用“key=value”格式,例如:

tasks:

- name: disable selinux

command: /sbin/setenforce 0

如果命令或指令碼的退出碼不為零,可以使用如下方式替代:

tasks:

- name: run this command and ignore the result

shell: /usr/bin/somecommand || /bin/true

或者使用ignore_errors來忽略錯誤資訊:

tasks:

- name: run this command and ignore the result

shell: /usr/bin/somecommand

ignore_errors: True

6。1。3 handlers

用於當關注的資源發生變化時採取一定的操作。

“notify”這個action可用於在每個play的最後被觸發,這樣可以避免多次有改變發生時每次都執行指定的操作,取而代之,僅在所有的變化發生完成後一次性地執行指定操作。在notify中列出的操作稱為handler,也即notify中呼叫handler中定義的操作。

- name: template configuration file

template: alt="自動化運維中的萬人迷-ansible" data-isLoading="0" src="/static/img/blank.gif" data-src=template。j2 dest=/etc/foo。conf

notify:

- restart memcached

- restart apache

handler是task列表,這些task與前述的task並沒有本質上的不同。

handlers:

- name: restart memcached

service: name=memcached state=restarted

- name: restart apache

service: name=apache state=restarted

案例:

heartbeat。yaml

- hosts: hbhosts

remote_user: root

tasks:

- name: ensure heartbeat latest version

yum: name=heartbeat state=present

- name: authkeys configure file

copy: alt="自動化運維中的萬人迷-ansible" data-isLoading="0" src="/static/img/blank.gif" data-src=/root/hb_conf/authkeys dest=/etc/ha。d/authkeys

- name: authkeys mode 600

file: path=/etc/ha。d/authkeys mode=600

notify:

- restart heartbeat

- name: ha。cf configure file

copy: alt="自動化運維中的萬人迷-ansible" data-isLoading="0" src="/static/img/blank.gif" data-src=/root/hb_conf/ha。cf dest=/etc/ha。d/ha。cf

notify:

- restart heartbeat

handlers:

- name: restart heartbeat

service: name=heartbeat state=restarted

七、roles

ansilbe自1。2版本引入的新特性,用於層次性、結構化地組織playbook。roles能夠根據層次型結構自動裝載變數檔案、tasks以及handlers等。要使用roles只需要在playbook中使用include指令即可。簡單來講,roles就是透過分別將變數、檔案、任務、模板及處理器放置於單獨的目錄中,並可以便捷地include它們的一種機制。角色一般用於基於主機構建服務的場景中,但也可以是用於構建守護程序等場景中。

一個roles的案例如下所示:

site。yml

webservers。yml

dbservers。yml

roles/

common/

files/

templates/

tasks/

handlers/

vars/

meta/

webservers/

files/

templates/

tasks/

handlers/

vars/

meta/

而在playbook中,可以這樣使用roles:

——-

- hosts: webservers

roles:

- common

- webservers

也可以向roles傳遞引數,例如:

——-

- hosts: webservers

roles:

- common

- { role: foo_app_instance, dir: ‘/opt/a’, port: 5000 }

- { role: foo_app_instance, dir: ‘/opt/b’, port: 5001 }

甚至也可以條件式地使用roles,例如:

——-

- hosts: webservers

roles:

- { role: some_role, when: “ansible_os_family == ‘RedHat’” }

7。1 建立role的步驟

(1) 建立以roles命名的目錄;

(2) 在roles目錄中分別建立以各角色名稱命名的目錄,如webservers等;

(3) 在每個角色命名的目錄中分別建立files、handlers、meta、tasks、templates和vars目錄;用不到的目錄可以建立為空目錄,也可以不建立;

(4) 在playbook檔案中,呼叫各角色;

7。2 role內各目錄中可用的檔案

tasks目錄:至少應該包含一個名為main。yml的檔案,其定義了此角色的任務列表;此檔案可以使用include包含其它的位於此目錄中的task檔案;

files目錄:存放由copy或script等模組呼叫的檔案;

templates目錄:template模組會自動在此目錄中尋找Jinja2模板檔案;

handlers目錄:此目錄中應當包含一個main。yml檔案,用於定義此角色用到的各handler;在handler中使用include包含的其它的handler檔案也應該位於此目錄中;

vars目錄:應當包含一個main。yml檔案,用於定義此角色用到的變數;

meta目錄:應當包含一個main。yml檔案,用於定義此角色的特殊設定及其依賴關係;ansible 1。3及其以後的版本才支援;

default目錄:為當前角色設定預設變數時使用此目錄;應當包含一個main。yml檔案;

八、Tags

tags用於讓使用者選擇執行playbook中的部分程式碼。ansible具有冪等性,因此會自動跳過沒有變化的部分,即便如此,有些程式碼為測試其確實沒有發生變化的時間依然會非常地長。此時,如果確信其沒有變化,就可以透過tags跳過此些程式碼片斷。

九、Jinja2相關

9。1 字面量

表示式最簡單的形式就是字面量。字面量表示諸如字串和數值的 Python 物件。下面 的字面量是可用的:

“Hello World”:

雙引號或單引號中間的一切都是字串。無論何時你需要在模板中使用一個字 符串(比如函式呼叫、過濾器或只是包含或繼承一個模板的引數),它們都是 有用的。

42 / 42。23:

直接寫下數值就可以建立整數和浮點數。如果有小數點,則為浮點數,否則為 整數。記住在 Python 裡, 42 和 42。0 是不一樣的。

[‘list’, ‘of’, ‘objects’]:

一對中括號括起來的東西是一個列表。列表用於儲存和迭代序列化的資料。例如 你可以容易地在 for 迴圈中用列表和元組建立一個連結的列表:

    {% for href, caption in [(‘index。html’, ‘Index’), (‘about。html’, ‘About’),

    (‘downloads。html’, ‘Downloads’)] %}

  • {{ caption }}
  • {% endfor %}

(‘tuple’, ‘of’, ‘values’):

元組與列表類似,只是你不能修改元組。如果元組中只有一個項,你需要以逗號 結尾它。元組通常用於表示兩個或更多元素的項。更多細節見上面的例子。

{‘dict’: ‘of’, ‘key’: ‘and’, ‘value’: ‘pairs’}:

Python 中的字典是一種關聯鍵和值的結構。鍵必須是唯一的,並且鍵必須只有一個 值。字典在模板中很少使用,罕用於諸如 xmlattr() 過濾器之類。

true / false:

true 永遠是 true ,而 false 始終是 false 。

9。2 算術運算

Jinja 允許你用計算值。這在模板中很少用到,但是為了完整性允許其存在。支援下面的 運算子:

+

把兩個物件加到一起。通常物件是素質,但是如果兩者是字串或列表,你可以用這 種方式來銜接它們。無論如何這不是首選的連線字串的方式!連線字串見 ~ 運算子。 {{ 1 + 1 }} 等於 2 。

-

用第一個數減去第二個數。 {{ 3 - 2 }} 等於 1 。

/

對兩個數做除法。返回值會是一個浮點數。 {{ 1 / 2 }} 等於 {{ 0。5 }} 。

//

對兩個數做除法,返回整數商。 {{ 20 // 7 }} 等於 2 。

%

計算整數除法的餘數。 {{ 11 % 7 }} 等於 4 。

*

用右邊的數乘左邊的運算元。 {{ 2 * 2 }} 會返回 4 。也可以用於重 復一個字串多次。 {{ ‘=’ * 80 }} 會列印 80 個等號的橫條。

**

取左運算元的右運算元次冪。 {{ 2**3 }} 會返回 8 。

10。3 比較運算子

==

比較兩個物件是否相等。

!=

比較兩個物件是否不等。

>

如果左邊大於右邊,返回 true 。

>=

如果左邊大於等於右邊,返回 true 。

<

如果左邊小於右邊,返回 true 。

<=

如果左邊小於等於右邊,返回 true 。

10。4 邏輯運算子

對於 if 語句,在 for 過濾或 if 表示式中,它可以用於聯合多個表示式:

and

如果左運算元和右運算元同為真,返回 true 。

or

如果左運算元和右運算元有一個為真,返回 true 。

not

對一個表示式取反(見下)。

(expr)

表示式組。