Ansible 是一個配置管理和應用部署工具,功能相似於目前業界的配置管理工具 Chef,Puppet,Saltstack。Ansible 是經過 Python 語言開發。Ansible 平臺由 Michael DeHaan 建立,他同時也是知名軟件 Cobbler 與 Func 的做者。Ansible 的第一個版本發佈於 2012 年 2 月,相比較其它同類產品來講,Ansible 仍是很是年輕的,但這並不影響他的蓬勃發展與你們對他的熱愛。mysql
Ansible 默認經過 SSH 協議管理機器,因此 Ansible 不須要安裝客戶端程序在服務器上。您只須要將 Ansible 安裝在一臺服務器,在 Ansible 安裝完後,您就能夠去管理控制其它服務器。不須要爲它配置數據庫,Ansible 不會以 daemons 方式來啓動或保持運行狀態。Ansible 的目標有以下:linux
- 自動化部署應用
- 自動化管理配置
- 自動化的持續交付
- 自動化的(AWS)雲服務管理。
根據 Ansible 官方提供的信息,當前使用 Ansible 的用戶有:evernote、rackspace、NASA、Atlassian、twitter 等。web
回頁首sql
安裝前的準備
Ansible 服務器需求
當前 Ansible 能夠運行在安裝了 Python2.6 的任何機器上(暫不支持 windows 機器作中心控制服務器),這包括 RedHat,Debian,CentOS,OS X,BSDS 等等。數據庫
被管理服務器需求
在被管理節點服務器上,須要安裝 Python2.4 或更高的 Python 版本,若是在遠程機器上運行的 Python 小於 Python2.5 的話,您將須要安裝 python-simplejson。若是在遠程機器的啓用了 SELinux,你還須要安裝 libselinux-python。npm
回頁首json
安裝 Ansible
若是你是 CentOS/RedHat、Debian、Ubuntu 的用戶,您可使用系統包管理器 yum 或 apt-get 安裝(CentOS/RedHat 須要安裝 epel 包才能經過 yum 安裝)。不過在這裏,筆者強烈建議您使用 pip 安裝 Ansible。那麼什麼是 pip 呢? pip 是一個 Python 包安裝和管理工具,功能相似 Node.js 的 npm、Ruby 的 gem。經過 pip 能夠很是方便對 Python 包進行安裝、升級、刪除等管理操做。前面介紹過 Ansible 是使 Python 開發,那麼天然 Ansible 也能夠經過 pip 安裝。如想了解 pip 的更多詳情,請訪問 https://pypi.python.org/pypi/pip/。windows
經過 pip 安裝 Ansible
在使用 pip 以前,請確保您的系統已經安裝 Pthon 的 setuptools 包服務器
yum – y install python-setuptools
安裝 pip
easy_install pip
經過 pip 安裝 Ansible
pip install ansbile
怎麼樣?經過 pip 安裝 Ansible 是否是很簡單,只要簡單的三個步驟,就完成了操做。只要支持 Python 的操做系統,均可以使用 pip 安裝。
好的,如今你若是照着作的話,你已經在你的服務器上安裝好了 Ansible。
前面介紹過 Ansible 是經過 SSH 協議管理機器,Ansible 默認使用 SSH Keys 方式通訊,這個也是 Ansible 官方極力推薦的方法,可是若是您想使用密碼的話,固然也是能夠的。爲了啓用密碼驗證,使用 --ask-pass 選項,在本文中,筆者採用密鑰驗證方式進行演示,讀者能夠根據的須要選擇使用密碼驗證仍是密鑰驗證。
Ansible 實戰
使用 Ansible 在遠程服務器上執行命令
在安裝好 Ansible 後,先經過使用 Ansible 在其它服務器上執行一條命令來確認 Ansible 服務器與其它服務器的連通性。在執行 Ansible 管理服務器前,須要要配置服務器信息。Ansible 使用文件來存儲您要管理的服務器信息,這個文件在 Ansible 中叫清單文件,默認的清單文件存放在 /etc/ansible/hosts , 若是這個文件不存在,您能夠新建該文件。固然您也能夠在執行 Ansible 命令時執行清單文件的路徑。清單文件的格式相似於 INI 文件,以下:
[webserver] 192.168.1.1 192.168.1.2 [databaseServer] 192.168.1.10 192.168.1.11
中括號符號中的是組名,組名能夠自定義,但建議使用一個有說明意義的名稱,如 webserver、databaseServer,組名後是各組內的成員 IP。下面筆者經過 Ansible 在遠程服務器上執行一條命令:
ansible webserver -a "whoami"
圖 1.ansible 執行命令結果
![圖 1:ansible 執行命令結果](http://static.javashuo.com/static/loading.gif)
從 Ansible 的執行結果,咱們知道 Ansible 服務器與遠程服務器的連通是沒有問題,而且執行命令成功。若是您想指定清單文件的路徑,那麼使用 – i 參數,加上文件路徑便可,以下:
ansible webserver – i /etc/ansible/other-hosts – a「whoami」
以上是一條 ad-hoc 命令,也即臨時執行命令。ad-hoc 是 Ansible 裏的一個概念,經過使用 ad-hoc 您能夠快速的完成命令的操做,若是你的操做只包含命令操做,而沒有配置管理的內容的話,那麼強烈建議使用 ad-hoc 命令,如文件傳輸、包管理、用戶和組的管理、服務管理等等。
使用 Ansible 管理 MySQL 複製
Ansible 附帶了很是多的模塊,Ansible 將之稱爲「模塊庫」。模塊能夠在遠程服務器上直接執行,也能夠經過 Playbooks 執行,關於 Playbooks 將會在後面作介紹。Ansible 的模塊很是豐富,包括雲服務管理、文件、數據庫、命令、網絡等各個方面。筆者在這裏演示若是經過 Ansible 其中的一個模塊「mysql_replication」來管理 MySQL 的複製。首先在配置好 MySQL master 服務器與 slave 服務器的 MySQL 環境,使其知足 MySQL 複製的配置需求,而且 MySQL 主從服務器都要安裝 Python 的 MySQLdb 模塊,經過 Ansible 在主、從服務器上安裝 MySQLdb。
ansible databaseServer -m yum -a "name=MySQL-python state=present"
在 MySQL master 服務器上建立 MySQL 複製賬號
mysql -uroot -p mysql > GRANT REPLICATION SLAVE,REPLICATION CLIENT ON *.* TO 'repl'@'192.168.1.%' IDENTIFIED BY '123456'; mysql > flush privileges; mysql > quit;
在 MySQL master 服務器打開 mysql 的二進制日誌功能 , 在 /etc/my.cnf 文件的 [mysqld] 部分添加如下內容:
server-id = 1
log-bin=mysql-bin
重啓 MySQL , 使設置生效。
/etc/init.d/mysqld restart
修改 MySQL slave 數據庫的 mysql 配置文件 , 在 /etc/my.cnf 文件的 [mysqld] 部分添加如下內容:
server-id = 2
重啓 MySQL , 使設置生效。
/etc/init.d/mysqld restart
經過 Ansible 的 mysql_replication 模塊獲取到 MySQL master 服務器的狀態
ansible mysql-master -m mysql_replication -a "login_user=root login_password=123456 mode=getmaster"
圖 2.獲取 MySQL Master 服務器狀態
![圖 2:獲取 MySQL Master 服務器狀態](http://static.javashuo.com/static/loading.gif)
Ansible 的結果都是採用的 json 格式顯示,結果輸出了 MySQL 的正在使用的二進制日誌文件名稱以及 Position 的值,記錄這兩個值。
經過 Ansible 的 mysql_replication 模塊在 MySQL Slave 服務器上指定 MySQL master 服務器及設置複製信息。
ansible mysql-slave -m mysql_replication – a "login_user=root login_password=123456 mode=changemaster master_host='172.16.64.147' master_user=repl master_password=123456 master_log_file=mysql.000003 master_log_pos=711"
圖 3.MySQL Change Master 執行結果:
![圖 3:MySQL Change Master 執行結果:](http://static.javashuo.com/static/loading.gif)
在 MySQL 從服務器上啓動複製
ansible mysql-slave -m mysql_replication -a "login_user=root login_password=123456 mode=startslave"
圖 4.在 MySQL Slave 開啓複製執行結果:
![圖 4:在 MySQL Slave 開啓複製執行結果:](http://static.javashuo.com/static/loading.gif)
查看 MySQL 複製狀態
ansible mysql-slave -m mysql_replication -a "login_user=root login_password=123456 mode=getslave"
圖 5.查看 MySQL 複製狀態
![圖 5:查看 MySQL 複製狀態](http://static.javashuo.com/static/loading.gif)
請注意圖中紅框選處,顯示 Slave_IO_Running 和 Slave_SQL_Running 的值均爲「Yes」,Slave_IO_State 的狀態爲「waiting for master to send event」,這表示剛剛經過 ansible 創建 MySQL 主從服務器之間的複製是正常的。如今您就能夠嘗試在 MySQL 主服務器上添加一些數據,看看會不會同步同步從服務器上。
經過 Ansible playbooks 管理 MySQL 複製
經過使用 Ansible 的 ad-hoc 命令行指令來使用模塊命令是否是感受很便捷,但咱們仍然要手工去作不少事情,如安裝 MySQL、配置 MySQL 複製環境等。雖然這些也能夠經過 ad-hoc 命令行完成,可是若是我下次還要在其它服務器上配置 MySQL 複製,那麼我又得重複敲一遍指令,若是是這樣的話,那麼這將是同樣很是糟糕的事情。那麼 Ansible 有沒有辦法解決這個問題呢?固然是有,Ansible 的 Playbooks 就能夠很好的解決這個問題,Playbooks 翻譯成中文就是劇本的意思,顧名思義,就是將全部的操做按照約定好的規則在文件中定義好作什麼,而後再按照劇本演出一部大劇。Playbooks 很是適合部署複雜應用及配置的重複使用。Playbooks 聲明配置,並你設置好的配置推送到指定的遠程主機應用。Playbooks 與 saltstack 的 SLS(Salt State Tree)、puppet 的配置管理相同。
Playbooks 使用 YAML 格式,語法應用簡明易懂。如下筆者就使用 Ansilbe 來實現 MySQL 的安裝、MySQL 複製環境的配置、MySQL 複製設置。
圖 6.目錄結構圖
![圖 6:目錄結構圖](http://static.javashuo.com/static/loading.gif)
爲了有效的組織和管理 Ansible 中的文件,筆者使用了 Ansible Playbook 的 roles 特性,這個特性在 Ansible 版本 1.2 或以後的版本中有效。筆者在 /etc/ansible 目錄下創建了一個 roles 目錄用於存放 Playbooks 定義的各個角色,這個是 Ansible 官方定義的默認 roles 目錄,固然你能夠添加或修改 roles 的目錄路徑,經過修改 /etc/ansible/ansible.cfg 文件中的 roles_path 值。roles 目錄下是各個應用的目錄名稱,各目錄以應用名稱命令,這個名稱能夠自定義,但最好是設置一個有意義的名稱,如本例中的 mysql,mysql 命令下包括了 defaults、handlers、meta、tasks、templates、vars 目錄,分別對應不一樣的功能,這幾個目錄的命名都是 Ansible 官方定義好的,不能夠修改,各目錄功能說明見表 1。默認 Ansible 只處理各目錄下文件名爲 main.yml 中定義的操做,若是您有多個文件,能夠在 main.yml 文件中 include 其它文件。
表 1.各目錄功能說明
目錄名 | 說明 |
---|---|
defaults | 默認變量存放目錄 |
handlers | 處理程序(當發生改變時須要執行的操做) |
meta | 角色依賴關係處理 |
tasks | 具體執行的任務操做定義 |
templates | 模板文件存放目錄 |
vars | 變量文件目錄 |
代碼 1.defaults 目錄的 main.yml 信息
cat /etc/ansible/roles/mysql/defaults/main.yml --- mysql_port: 3306 mysql_bind_address: "0.0.0.0" mysql_root_db_pass: 123456 mysql_db: - name: foo replicate: yes - name: bar replicate: no mysql_users: - name: jack pass: 123456 priv: "*.*:ALL" mysql_repl_user: - name: repl pass: 123456 mysql_repl_role: master mysql_db_id: 7
該文件包含了一些與 MySQL 相關的一些默認的變量信息,在這裏設置了 MySQL 的端口、綁定地址,root 用戶的密碼,及新增的數據庫、用戶、複製用戶信息。
代碼 2.handlers 目錄的 main.yml 信息
cat /etc/ansible/roles/mysql/handlers/main.yml --- - name: restart mysql service: name={{ mysql_service }} state=restarted
handlers 目錄中的 main.yml 文件包含的操做是執行完 tasks 以後服務器發生變化以後可供調用 的操做,本例中是重啓 MySQL 操做
代碼 3.tasks 目錄裏的 main.yml 信息
--- - name: Add the OS specific variables include_vars: "{{ ansible_os_family }}.yml" - name: Install the mysql packages in Redhat derivatives yum: name={{ item }} state=installed with_items: mysql_pkgs when: ansible_os_family == 'RedHat' - name: Install the mysql packages in Debian derivatives apt: name={{ item }} state=installed update_cache=yes with_items: mysql_pkgs environment: env when: ansible_os_family == 'Debian' - name: Copy the my.cnf file template: src=my.cnf.{{ ansible_os_family }}.j2 dest={{ mysql_conf_dir }}/my.cnf notify: - restart mysql - name: Start the mysql services Redhat service: name={{ mysql_service }} state=started enabled=yes - name: update mysql root password for all root accounts mysql_user: name=root host={{ item }} password={{ mysql_root_db_pass }} with_items: - "{{ ansible_hostname }}" - 127.0.0.1 - ::1 - localhost when: ansible_hostname != 'localhost' - name: update mysql root password for all root accounts mysql_user: name=root host={{ item }} password={{ mysql_root_db_pass }} with_items: - 127.0.0.1 - ::1 - localhost when: ansible_hostname == 'localhost' - name: ensure anonymous users are not in the database mysql_user: name='' host={{ item }} login_user=root login_password={{ mysql_root_db_pass }} state=absent with_items: - localhost - "{{ ansible_hostname }}" - name: remove the test database mysql_db: name=test login_user=root login_password={{ mysql_root_db_pass }} state=absent - name: Create the database's mysql_db: name={{ item.name }} login_user=root login_password={{ mysql_root_db_pass }} state=present with_items: mysql_db when: mysql_db|lower() != 'none' - name: Create the database users mysql_user: login_user=root login_password={{ mysql_root_db_pass }} name={{ item.name }} password={{ item.pass|default("foobar") }} priv={{ item.priv|default("*.*:ALL") }} state=present host={{ item.host | default("localhost") }} with_items: mysql_users when: mysql_users|lower() != 'none' - name: Create the replication users mysql_user: login_user=root login_password={{ mysql_root_db_pass }} name={{ item.name }} host="%" password={{ item.pass|default("foobar") }} priv=*.*:"REPLICATION SLAVE" state=present with_items: mysql_repl_user when: mysql_repl_role == 'master' - name: Check if slave is already configured for replication mysql_replication: login_user=root login_password={{ mysql_root_db_pass }} mode=getslave ignore_errors: true register: slave when: mysql_repl_role == 'slave' - name: Ensure the hostname entry for master is available for the client. lineinfile: dest=/etc/hosts regexp="{{ mysql_repl_master }}" line="{{ mysql_repl_master + " " + hostvars[mysql_repl_master].ansible_default_ipv4.address }}" state=present when: slave|failed and mysql_repl_role == 'slave' and mysql_repl_master is defined - name: Get the current master servers replication status mysql_replication: login_user=root login_password= {{ mysql_root_db_pass }} mode=getmaster delegate_to: "{{ mysql_repl_master }}" register: repl_stat when: slave|failed and mysql_repl_role == 'slave' and mysql_repl_master is defined - name: Change the master in slave to start the replication mysql_replication: login_user=root login_password={{ mysql_root_db_pass }} mode=changemaster master_host={{ mysql_repl_master }} master_log_file={{ repl_stat.File }} master_log_pos={{ repl_stat.Position }} master_user={{ mysql_repl_user[0].name }} master_password={{ mysql_repl_user[0].pass }} when: slave|failed and mysql_repl_role == 'slave' and mysql_repl_master is defined - name: start slave in slave to start the replication mysql_replication: login_user=root login_password={{ mysql_root_db_pass }} mode=startslave when: slave|failed and mysql_repl_role == 'slave' and mysql_repl_master is defined
tasks 目錄中的 main.yml 包括了 playbooks 中全部要執行操做,每個操做都會有一個名稱,用於簡單的描述本次的操做內容,本例中的 tasks 包括了:
- MySQL 安裝;
- MySQL 管理員用戶 root 的密碼修改;
- MySQL 配置文件的生成;
- 測試庫的刪除;
- 空用戶的刪除;
- 數據庫的建立(若須要);
- 數據庫用戶的建立及賦權(苦須要);
- 複製用戶建立及密碼設置;
- 獲取 master 狀態;
- Slave 鏈接 master 並開啓複製。
Ansible playbooks 中的每一個任務是按序依次執行的,這個你們能夠看 ansible playbooks 的執行過程。
代碼 4.templates 目錄中的 my.cnf.RedHat.j2
cat /etc/ansible/roles/mysql/templates/my.cnf.RedHat.j2 [mysqld] datadir=/var/lib/mysql socket=/var/lib/mysql/mysql.sock user=mysql # Disabling symbolic-links is recommended to prevent assorted security risks symbolic-links=0 port={{ mysql_port }} bind-address={{ mysql_bind_address }} log_bin = mysql-bin server-id = {{ mysql_db_id }} {% if mysql_repl_role == 'master' %} #log_bin = mysql-bin expire_logs_days = 10 max_binlog_size = 100M {% for i in mysql_db %} {% if i.replicate|default(1) %} binlog_do_db = {{ i.name }} {% endif %} {% endfor %} {% for i in mysql_db %} {% if not i.replicate|default(1) %} binlog_ignore_db = {{ i.name }} {% endif %} {% endfor %} {% endif %} [mysqld_safe] log-error=/var/log/mysqld.log pid-file=/var/run/mysqld/mysqld.pid
templates 目錄中包括了一個 MySQL 的配置文件樣本。
代碼 5.vars 目錄中的 main.yml
cat /etc/ansible/roles/mysql/vars/main.yml --- env: RUNLEVEL: 1
代碼 6.vars 目錄中的 RedHat.yml
cat /etc/ansible/roles/mysql/vars/RedHat.yml --- mysql_pkgs: - libselinux-python - mysql-server - MySQL-python mysql_service: mysqld mysql_conf_dir: "/etc/"
vars 目錄中是這次操做所涉及到的變量信息。
代碼 7.ansible 執行 MySQL 複製配置的 playbook 配置文件 mysql_repl.yml
cat /etc/ansible/mysql_repl.yml - hosts: mysql-master roles: - {role: mysql, mysql_db: none,mysql_users: [{name: jack, pass: 123456, priv: "*.*:ALL"}],mysql_db_id: 1008 } - hosts: mysql-slave roles: - {role: mysql, mysql_db: none, mysql_users: none,mysql_repl_role: slave, mysql_repl_master: 192.168.1.10,mysql_db_id:1009, mysql_repl_user: [{name: repl, pass: 123456}] }
安裝並配置 MySQL 服務器,並完成 MySQL 主從複製設置。
在 /etc/ansible/hosts 文件中添加服務器信息
cat /etc/ansible/hosts [mysql-master] 192.168.1.10 [mysql-slave] 192.168.1.11
使用 Ansible playbook 配置 MySQL 複製
ansible-playbook mysql_repl.yml PLAY [mysql-master] *********************************************************** GATHERING FACTS *************************************************************** ok: [192.168.1.10] TASK: [mysql | Add the OS specific variables] ********************************* ok: [192.168.1.10] TASK: [mysql | Install the mysql packages in Redhat derivatives] ************** changed: [192.168.1.10] => (item=libselinux-python,mysql-server,MySQL-python) TASK: [mysql | Install the mysql packages in Debian derivatives] ************** skipping: [192.168.1.10] TASK: [mysql | Copy the my.cnf file] ****************************************** ok: [192.168.1.10] TASK: [mysql | Start the mysql services Redhat] ******************************* changed: [192.168.1.10] TASK: [mysql | update mysql root password for all root accounts] ************** changed: [192.168.1.10] => (item=client001) changed: [192.168.1.10] => (item=127.0.0.1) changed: [192.168.1.10] => (item=::1) changed: [192.168.1.10] => (item=localhost) TASK: [mysql | update mysql root password for all root accounts] ************** skipping: [192.168.1.10] => (item=127.0.0.1) skipping: [192.168.1.10] => (item=::1) skipping: [192.168.1.10] => (item=localhost) TASK: [mysql | ensure anonymous users are not in the database] **************** changed: [192.168.1.10] => (item=localhost) ok: [192.168.1.10] => (item=client001) TASK: [mysql | remove the test database] ************************************** changed: [192.168.1.10] TASK: [mysql | Create the database's] ***************************************** changed: [192.168.1.10] => (item={'name': 'benz'}) changed: [192.168.1.10] => (item={'name': 'benz2'}) TASK: [mysql | Create the database users] ************************************* changed: [192.168.1.10] => (item={'pass': 'foobar', 'name': 'ben3', 'priv': '*.*:ALL'}) changed: [192.168.1.10] => (item={'name': 'ben2', 'pass': 'foo'}) TASK: [mysql | Create the replication users] ********************************** changed: [192.168.1.10] => (item={'name': 'repl', 'pass': 'foobar'}) TASK: [mysql | Check if slave is already configured for replication] ********** skipping: [192.168.1.10] TASK: [mysql | Ensure the hostname entry for master is available for the client.] *** skipping: [192.168.1.10] TASK: [mysql | Get the current master servers replication status] ************* skipping: [192.168.1.10] TASK: [mysql | Change the master in slave to start the replication] *********** skipping: [192.168.1.10] TASK: [mysql | start slave in slave to start the replication] ***************** skipping: [192.168.1.10] #### 限於篇幅,部分輸出省略 ####### PLAY RECAP ******************************************************************** 192.168.1.10 : ok=12 changed=8 unreachable=0 failed=0 192.168.1.11 : ok=17 changed=8 unreachable=0 failed=0
你們仔細看輸出,會發現 ansible 的 task 是按順序一個個依次執行,在執行每一個任務時,都會顯示該任務的名稱及狀態,這樣很是利於查看任務執行的狀態。而後在任務的最終結尾處(即 PLAY RECAP)是各個 task 的狀態統計總結,包含了各個 task 的執行狀況,如執行成功數,變動數、錯誤數。經過這裏能夠判斷 task 是否成功執行完成。
Ansible 與其它配置管理的對比
筆者選擇了目前幾款主流的與 Ansible 功能相似的配置管理軟件 Puppet、Saltstack,這裏所作的對比不針對各個軟件的性能做比較,只是對各個軟件的特性作個對比。具體內容見表 1:
表 2.Ansible Vs. puppet Vs. Saltstack 對比
Puppet | Saltstack | Ansible | |
---|---|---|---|
開發語言 | Ruby | Python | Python |
是否有客戶端 | 有 | 有 | 無 |
是否支持二次開發 | 不支持 | 支持 | 支持 |
服務器與遠程機器是否相互驗證 | 是 | 是 | 是 |
服務器與遠程機器通訊是否加密 | 是,標準 SSL 協議 | 是,使用 AES 加密 | 是,使用 OpenSSH |
平臺支持 | 支持 AIX、BSD、HP-UX、Linux、 MacOSX、Solaris、 Windows | 支持 BSD、Linux、Mac OS X、Solaris、 Windows | 支持 AIX、BSD、 HP-UX、 Linux、Mac OSX、Solaris |
是否提供 web ui | 提供 | 提供 | 提供,不過是商業版本 |
配置文件格式 | Ruby 語法格式 | YAML | YAML |
命令行執行 | 不支持,但可經過配置模塊實現 | 支持 | 支持 |
結束語:
Ansible 是一個新興的 IT 自動化管理工具。目前它的下載量已經超過了 100 萬。在 GitHub,它是排名前 10 位的 Python 項目。能夠預見 Ansible 的發展是不可限量。筆者在本文中使用 Anaible 來管理 MySQL 複製來向你們介紹了 Ansible 一些使用方法和應用場景,但願經過本文,可以讓你們都愛上這個超級有能量的系統自動化管理工具。