長久以來,IT 運維在企業內部一直是個耗人耗力的事情。隨着虛擬化的大量應用、私有云、容器的不斷普及,數據中心內部的壓力愈發增長。傳統的自動化工具,每每是面向於數據中心特定的一類對象,例如操做系統、虛擬化、網絡設備的自動化運維工具每每是不一樣的。那麼,有沒有一種數據中心級別的統一的自動化運維工具呢?html
答案就是 Ansible。和傳統的自動化工具 (如 Puppet)相比,Ansible 尤爲明顯的優點:前端
接下來,本文將針對 Ansible 這一開源 IT 自動化運維工具進行介紹,並經過實驗場景讓您瞭解 Ansible 的實際做用。node
Ansible 是一個簡便的 IT 自動化引擎。近期,Ansible 在 Github 上是一個很是熱門的開源項目,能夠參見下圖該項目的 Star、Fork 和 commits 數量。python
點擊查看大圖mysql
那麼,Ansible 可以納管(管理)哪些數據中心對象呢?經過查看 Ansible 的模塊(Modules,後文將具體介紹)可知,它幾乎支持數據中心的一切自動化,包括(不限於):linux
接下來,咱們來了解一下 Ansible 的相關組件,看它如何納管數據中心的對象。web
Ansible 的核心組件包括:Modules、Inventory、Playbook、Roles 和 Plugins。sql
Modules
咱們在 Linux 上書寫 Shell,須要調用 Linux 操做系統命令,如 ls、mv、chmod
等;在書寫 POJO 時,須要調用 Java 相關 Pattern。Linux 系統命令對 Shell 而言和 Java Pattern 對於 POJO 而言,都是被調用的模塊。Modules 就是使用 Ansible 進行自動化任務時調用的模塊。在工做方時,Ansible 首先鏈接(默認經過 SSH)被管理節點(多是服務器、公有云或、網絡設備等),而後向這些節點推送 Modules、執行這些 Modules,並在完成後刪除 Modules。
Modules 是 Ansible 的核心資產,有了 Modules,咱們才能調用這些 Modules 來完成咱們想要執行的自動化任務。舉個例子:selinux - Change policy and state of SELinux。這個 Module 對的做用是配置配置 SELinux 模式和策略。咱們能夠經過調用這個 Module,來配置 RHEL/CentOS 的 SELinux 模式(eforcing、permissive 或 disabled)。目前社區中 Modules 數量很是多、涵蓋範圍很是廣,而且以較快的速度進行增加。
Inventory
Inventory 是 Ansible 要管理對象的清單。在清單中,還能夠配置分組信息等。舉例以下:
1
2
3
4
5
6
7
|
[webservers]
www1.example.com
www2.example.com
[dbservers]
db0.example.com
db1.example.com
|
若是說 Modules 是咱們使用 Ansible 進行自動化任務時調用的模塊。那麼 Playbook 就是 Ansible 自動化任務的腳本(YAML 格式)。
Roles
Roles 是將 Playbook 分紅多個文件的主要機制。這簡化了編寫複雜的 Playbook,並使其更易於重用。經過 Roles 能夠將 Playbook 分解爲可重用的組件。
Plugins
Plugins 是加強 Ansible 核心功能的代碼。Ansible 附帶了許多方便的插件,若是這些插件不夠,咱們能夠編寫本身的插件。Ansible 自帶的 Plugins 以下圖所示:
Plugins 與 Modules 一塊兒執行 Playbook 任務所需的自動化任務的動做。當咱們使用 Modules 的時候若是須要調用 Plugins,Action Plugins 默認會被自動執行。
以上文提到的 Selinux Module 舉例。在書寫 Playbook 是要調用 Selinux Modules,完成對 RHEL/CentOS 的 SElinux 模式的配置,這就是一個 Action。這須要 Selinux Modules 調用 Action Plugins 一塊兒完成。
Plugins 的做用有不少,例如 Cache Plugins 的做用是實現後端緩存機制,容許 Ansible 存儲收集到的 inventory 源數據。
在本章中,咱們將介紹 Ansible 的基本使用場景,展現若是經過調用 Ansible Modules 執安裝 HTTP 並啓動 HTTP 服務。此外,咱們還會介紹如何調用 Ansible Roles 來執行自動化任務。
在本案例中,咱們調用兩個 Modules,yum 和 service,它們的做用以下:
在 Linux 系統中查看 Ansible 的版本,版本號爲 2.5.3:
在 Ansible 主機上配置 Inventory。配置兩個 Group:web 和 sql,分別包含一臺 Linux 被管系統。
1
2
3
4
5
|
[root@workstation-d04e ~]# cat /etc/ansible/hosts
[web]
servera.example.com
[sql]
serverb.example.com
|
配置 Ansible 主機到兩臺被管主機之間的無密碼 SSH 互信,以後,Ansible 能夠與兩臺被管主機正常通信:
經過 Ansible 調用 yum Modules,爲 Inventory 中的 Web Group 主機安裝 httpd:
手工確認 HTTP 成功安裝:
Ansible 調用 service Module,啓動 httpd:
檢查服務是否啓動:
經過本案例,咱們瞭解了 Modules 和 Inventory 的功能。接下來,我將展現 Roles 的功能。
Roles 能夠自行書寫,也可使用 Ansible Galaxy 官網上大量已經書寫好的 Roles。本案例將經過書寫 Playbook 調用 Roles,完成數據庫的安裝和配置。
登陸 Ansible Galaxy 網站,搜索並挑選一個質量評分高的 mysql Roles。
在 Ansible 主機上安裝 mysql Roles:
接下來,書寫一個 Playbook,調用 mysql Role,爲 Inventory 中定義的 Web 主機安裝 mysql。
1
2
3
4
5
|
[root@workstation-d04e ansible]# cat install-database.yml
- hosts: sql
name: Install the database server from an Ansible Galaxy role
roles:
- geerlingguy.mysql
|
執行 Playbook。至此,經過書寫 Playbook 調用 Roles,完成了數據庫的安裝和配置。
在本小節中,咱們將書寫 Playbook,完成以下任務:
首先建立 templates 目錄,在目錄中添加 httpd.conf.j2 模板:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
|
[root@workstation-d04e ansible]# mkdir templates
[root@workstation-d04e ansible]# cat httpd.conf.j2
ServerRoot "/etc/httpd"
Listen 80
Include conf.modules.d/*.conf
User apache
Group apache
ServerAdmin root@localhost
<
Directory
/>
AllowOverride none
Require all denied
</
Directory
>
DocumentRoot "/var/www/html"
<
Directory
"/var/www">
AllowOverride None
Require all granted
</
Directory
>
<
Directory
"/var/www/html">
Options Indexes FollowSymLinks
AllowOverride None
Require all granted
</
Directory
>
<
IfModule
dir_module>
DirectoryIndex index.html
</
IfModule
>
<
Files
".ht*">
Require all denied
</
Files
>
ErrorLog "logs/error_log"
MaxKeepAliveRequests {{ apache_max_keep_alive_requests }}
LogLevel warn
<
IfModule
log_config_module>
LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" combined
LogFormat "%h %l %u %t \"%r\" %>s %b" common
<
IfModule
logio_module>
LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\" %I %O" combinedio
</
IfModule
>
CustomLog "logs/access_log" combined
</
IfModule
>
<
IfModule
alias_module>
ScriptAlias /cgi-bin/ "/var/www/cgi-bin/"
</
IfModule
>
<
Directory
"/var/www/cgi-bin">
AllowOverride None
Options None
Require all granted
</
Directory
>
<
IfModule
mime_module>
TypesConfig /etc/mime.types
AddType application/x-compress .Z
AddType application/x-gzip .gz .tgz
AddType text/html .shtml
AddOutputFilter INCLUDES .shtml
</
IfModule
>
AddDefaultCharset UTF-8
<
IfModule
mime_magic_module>
MIMEMagicFile conf/magic
</
IfModule
>
EnableSendfile on
IncludeOptional conf.d/*.conf
|
而後,在目錄中添加 index.html.j2 模板:
1
2
3
4
5
6
7
|
[root@workstation-d04e templates]# cat index.html.j2
{{ apache_test_message }} {{ ansible_distribution }} {{ ansible_distribution_version }}
Current Host: {{ ansible_hostname }}
Server list:
{% for host in groups['web'] %}
{{ host }}
{% endfor %}
|
書寫 Playbook 以下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
|
[root@workstation-d04e ansible]# cat site.yml
---
- hosts: web
name: Install the web server and start it
become: yes
vars:
httpd_packages:
- httpd
- mod_wsgi
apache_test_message: This is a test message
apache_max_keep_alive_requests: 115
tasks:
- name: Install the apache web server
yum:
name: "{{ item }}"
state: present
with_items: "{{ httpd_packages }}"
notify: restart apache service
- name: Generate apache's configuration file from jinga2 template
template:
src: templates/httpd.conf.j2
dest: /etc/httpd/conf/httpd.conf
notify: restart apache service
- name: Generate a basic homepage from jinga2 template
template:
src: templates/index.html.j2
dest: /var/www/html/index.html
- name: Start the apache web server
service:
name: httpd
state: started
enabled: yes
handlers:
- name: restart apache service
service:
name: httpd
state: restarted
enabled: yes
|
咱們對 Playbook 作簡單的分析:
在上面的 Playbook 中,還用到了 Handlers 語法,以在變動時運行操做。Playbooks 有一個可用於響應變化的事件系統。當任務執行結束,notify(在 Playbook 中的每一個任務塊結束時以事件的方式通知 Handlers,從而觸發 handler 中定義的任務。)會觸發名字爲 restart apache service 的 Handlers。在 Ansible 中,即便多個任務都有 notify 的定義,但一個 Playbook 中,handler 只被觸發一次。這個 handler 的做用是調用 Module service 重啓 httpd 服務。
接下來,執行寫好的 Playbook,並觀察執行過程。輸出以下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
[root@workstation-d04e ansible]# ansible-playbook site.yml
PLAY [Install the web server and start it] ********************************************************
TASK [Gathering Facts] ****************************************************************************
ok: [servera.example.com]
TASK [Install the apache web server] **************************************************************
changed: [servera.example.com] => (item=[u'httpd', u'mod_wsgi'])
TASK [Generate apache's configuration file from jinga2 template] **********************************
changed: [servera.example.com]
TASK [Generate a basic homepage from jinga2 template] *********************************************
changed: [servera.example.com]
TASK [Start the apache web server] ****************************************************************
changed: [servera.example.com]
RUNNING HANDLER [restart apache service] **********************************************************
changed: [servera.example.com]
PLAY RECAP ****************************************************************************************
servera.example.com : ok=6 changed=5 unreachable=0 failed=0
|
Playbook 執行成功之後,經過 curl 驗證 Apache 的配置。
經過本章,相信您已經瞭解瞭如何經過 Modules、Roles 來執行簡單的自動化任務。接下來,咱們將介紹如何經過 Ansible 執行較爲複雜的自動化任務。
在自動化的場景中,咱們一般會遇到較爲複雜的場景,而不是簡單在一個系統上部署或配置一個服務組件。接下來,咱們經過 Ansible 執行一個較爲複雜的自動化任務——部署一個三層應用,其中包括:一個前端(HAproxy)、兩個 app 服務器(Tomcat)、一個數據庫(Postgresql)。架構圖以下:
因爲本案例較爲複雜,爲了增長 Playbook 的可讀性、可重複利用性,我用三個 Roles 完成安裝配置 HAproxy、Tomcat 和 Postgres,而後用一個主 Playbook 調用這個三個 Roles。
清單 8 中,咱們先定義整個自動化任務的主 playbook:main.yml:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
|
[root@bastion 3tier-good]# cat main.yml
---
# Setup front-end load balancer tier
- name: setup load-balancer tier
hosts: frontends
become: yes
roles:
- {name: base-config, tags: base-config}
- {name: lb-tier, tags: [lbs, haproxy]}
# Setup application servers tier
- name: setup app tier
hosts: apps
become: yes
gather_facts: false
roles:
- {name: base-config, tags: base-config}
- {name: app-tier, tags: [apps, tomcat]}
# Setup database tier
- name: setup database tier
become: yes
hosts: appdbs
roles:
- {name: base-config, tags: base-config}
- {name: geerlingguy.postgresql, tags: [dbs, postgres]}
|
接下來,書寫三個 Roles。因爲篇幅有限,僅以配置 load-balancer tier Roles 爲例進行分析。
在 frontends group 的主機上,執行兩個 Roles:base-config 和 lb-tier。咱們能夠查看這兩個 Roles 的具體內容。先查看 base-config 的目錄結構:
查看 tasks 下的 main.yaml:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
---
# Initial, common, system setup steps
- name: enable sudo without tty for some ansible commands
replace:
path: /etc/sudoers
regexp: '^Defaults\s*requiretty'
replace: 'Defaults !requiretty'
backup: yes
- name: enable repos
template:
src: repos_template.j2
dest: /etc/yum.repos.d/open_three-tier-app.repo
mode: 0644
#- name: setup hostname
# hostname:
# name: "{{ inventory_hostname }}"
- name: install base tools and packages
yum:
name: "{{ item }}"
state: latest
with_items:
- httpie
- python-pip
|
上面 Playbook 執行的操做以下:
^Defaults\s*requiretty
替換爲 Defaults !requiretty
。inventory_hostname
變量的賦值。接下來,咱們查看 lb-tier 的目錄結構:
查看 tasks 目錄下的 main.yml:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
---
- name: install {{ payload }}
yum:
name: "{{ payload }}"
state: latest
- name: enable {{ payload }} at boot
service:
name: "{{ payload }}"
enabled: yes
- name: configure haproxy to load balance over app servers
template:
src: haproxy.cfg.j2
dest: /etc/haproxy/haproxy.cfg
mode: 0644
- name: start {{ payload }}
service:
name: "{{ payload }}"
state: restarted
|
上面的 Playbook 完成的任務:
{{ payload }}
定義的服務。{{ payload }}
定義的服務查看./vars/main.yml
的內容,能夠看到變量{{ payload }}
的賦值爲 haproxy
。
1
2
|
[root@bastion vars]# cat main.yml
payload: haproxy
|
因此,lb-tier Roles 執行的任務就是安裝、配置、啓動 HAproxy 服務。
接下來,咱們執行 Playbook,首先確認相關節點能夠和 Ansible 正常通訊:
接下來,執行 Playbook,因爲篇幅有限咱們只列出前端配置部分的關鍵日誌:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
|
[root@bastion 3tier-good]# ansible-playbook -i /etc/ansible/hosts main.yml
PLAY [setup load-balancer tier] ************************************************
TASK [Gathering Facts] *********************************************************
ok: [frontend1.cefe.internal]
TASK [base-config : enable sudo without tty for some ansible commands] *********
ok: [frontend1.cefe.internal]
TASK [base-config : enable repos] **********************************************
changed: [frontend1.cefe.internal]
TASK [base-config : install base tools and packages] ***************************
[DEPRECATION WARNING]: Invoking "yum" only once while using a loop via
squash_actions is deprecated. Instead of using a loop to supply multiple items
and specifying `name: "{{ item }}"`, please use `name: ['httpie', 'python-
pip']` and remove the loop. This feature will be removed in version 2.11.
Deprecation warnings can be disabled by setting deprecation_warnings=False in
ansible.cfg.
changed: [frontend1.cefe.internal] => (item=[u'httpie', u'python-pip'])
PLAY RECAP *********************************************************************
app1.cefe.internal : ok=8 changed=7 unreachable=0 failed=0
app2.cefe.internal : ok=8 changed=7 unreachable=0 failed=0
appdb1.cefe.internal : ok=26 changed=13 unreachable=0 failed=0
frontend1.cefe.internal : ok=8 changed=6 unreachable=0 failed=0
|
能夠看到任務的執行邏輯與咱們前文介紹的內容是一致的。
接下來,驗證部署的應用。經過 RESTClient 向前端發起 POST 請求,能夠看到負載均衡的效果。
第一次請求,響應返回爲 Tomcat app1:
第二次請求,響應返回爲 Tomcat app2:
至此,咱們實現了經過 Ansible 部署一個三層應用的工做。
經過本文,相信您對 Ansible 及其核心組件的使用有了一些瞭解。隨着 Ansible 社區愈來愈受重視、 Modules 數量的迅速增長,Ansible 對整個數據中心的自動化管理能力愈來愈強。