ansible是一款自動化運維工具,基于python開發,他是基于各個模塊來工作的,主要由以下幾個組件:
- connection plugins:用來和需要操作的遠程主機通信的,由于ansible是無agent方式,所以需要一種連接方式,一般使用ssh來作為通信方式;
- host inventory:用來指定需要操作的遠程主機ip地址,配置文件默認為/etc/ansible/hosts;
- playbook:劇本,就是將事先定義好的需要在哪些遠程主機上執行什么操作,寫到playbook文件中,然后讓ansibles去讀取劇本文件并操作;
- core modules:核心模塊,ansible是通過模塊去到遠程主機上執行操作的
- custom modules:自定義模塊,除了自帶的模塊,用戶也可以自定義模塊
- plugins:一些功能插件,用來實現日志,郵件等功能
結構圖如下:
ansible命令
ansible命令是命令行工具,不需要讀取playbook文件
ansible :
- -m:指名模塊
- -a:給模塊指定參數,寫在“”里邊,如果是命令就直接寫進去就行
- -C:測試執行,不真正執行
- -f:一批處理幾個,默認為5
- -i:指定hosts文件
- --list-hosts:列出這次操作對哪些主機執行,只是列出
- --syntax-check:檢查語法
- -t:在文件中定義tags后使用這個選項只運行tags那一部分
- -c:連接方式 smart為默認,智能選擇合適的方式
- -u USERNAME:連接時使用的用戶名,默認為none
- -s:sudo
- -S:su
ansible-playbook命令
運行playbook使用ansible-playbook命令,常用參數如下:
- --syntax-check:檢測語法
- -C:測試運行,檢查錯誤時候使用,不是真正在目標主機上運行
- --list-hosts:顯示要執行的主機
- -t TAGS:使用文件中有標簽的地方,只執行標簽處的任務
-e VARS:使用變量
模塊
ansible是通過模塊來工作的,所以下面介紹各種功能的模塊:
首先用ansible-doc -l
命令可以列出模塊,可以看到有非常多的模塊可以使用,這里我們就據介紹一些比較重要的經常用的模塊,使用ansible-doc -s 模塊名
可以查看模塊的參數,用來定義模塊的期望值
模塊可以定義期望的目標狀態,而且操作必須時冪等的(重復數次的結果是相同的,都是定義的期望的狀態),如下為一些常用的模塊,例子都是使用命令行工具ansible來寫的:
1.group模塊
ansible all -m group -a "gid=3000 name=mygrp state=present system=no"
state=absent表示刪除組,present表示創建組,system=no表示不是系統組
2.user模塊
ansible all -m usre -a "uid=5000 name=testuser state=present groups=mygrp shell=/bin/tcsh"
表示用戶的uid為5000,用戶名為testuser,狀態為創建用戶,組名為mygrp,使用默認shell為/bin/tcsh
3.copy模塊
src選項的值為目錄時,最后帶/斜杠表示不復制目錄本身,不帶就表示復制目錄本身,src可以為空,用content表示所跟的內容直接生成到目標遠程主機文件,不跟dest就源是哪里,目標目錄就是哪里,remote_src表示使用遠程的源,state=absent表示刪除
ansible all -m copy -a "src=/etc/fstab dest=/tmp/test mode=600"
ansible all -m copy -a "content='haha\nhehe\n‘ dest =/tmp/hehe owner=testuser"
4.fetch模塊
將遠程主機復制到本機,偶爾會用到,不用指定多個主機
command模塊
遠程主機執行命令:chdir,切換目錄;executable,由哪個shell發起執行命令.command命令在執行時不適用shell,所以傳遞|或者>等參數時不識別,所以要使用shell模塊
6.shell模塊
解決了command的不識別,其他和command一樣
7.file模塊
用來創建文件(不推薦,一般用copy,content=空去創建),設定文件屬性,path=定義目標文件,state=file|directory
ansible all -m file -a "path=/tmp/hello.dir state=directory"
8.cron模塊
指定計劃任務,分時日月周和crontab一樣,要定義哪個就將哪個寫進去
ansible all -m cron -a "minute=*/3 job='/usr/sbin/ntpdate 172.16.0.1 &> /dev/null' name=None "
9.yum模塊
name=要安裝的包名;state=installd|removed|latest;disable_gpg_check=yes表示禁用gpgcheck
ansible all -m yum -a "name=nginx state=installed disabled_gpg_check=yes"
10.service模塊
name=服務名;enabled=yes表示開機啟用;runlevel在設置開機啟用后指定運行的級別;state=started|stopped|restarted|reloaded;
ansible all -m service -a "name=nginx enabled=true state=started"
11.script模塊
本地文件在遠程執行,遠程自己的腳本用shell模塊就行
ansible all -m script -a "腳本路徑"
12.setup模塊
用來收集對應主機的信息,信息中有很多內建變量可以直接使用,下面講到變量時候再說這個模塊
playbook
命令行方式不能復用,我們可以講每臺主機需要執行的操作寫入配置文件playbook,讓ansible去讀取并執行。
YAML格式:ansible相關文件的格式為YAML,是一種可讀性高,用來表達數據序列的格式,基本數據結構為:標量,數組,關聯數組.
playbook基本元素:
- hosts:運行指定任務的目標主機
remoute_user:遠程主機上執行任務的用戶
sudo_user:sudo的用戶 - tasks:任務
name:任務名字
module:模塊參數
tags:標簽,可以使用ansible-playbook 的-t選項來指定,只執行本標簽的任務 - handlers:特定條件觸發的任務,在一個任務中定義notify,然后這個任務觸發之后會通知這個handlers執行操作
還有一些別的元素我們在下面的大標題中挨個講解,我們先來看這些基礎元素的用法:
下面我們來創建一個playbook文件,并執行,注意,一定要縮進:
[root@localhost ansible]# vim /etc/ansible/test.yml
- hosts: 172.16.200.107
remote_user: root
tasks:
- name: install redis
yum: name=redis state=latest
- name: start redis
service: name=redis state=started
然后去檢測
[root@localhost ansible]# ansible-playbook --syntax-check test.yml
playbook: test.yml
[root@localhost ansible]# ansible-playbook -C test.yml
PLAY [172.16.200.107] **********************************************************
TASK [setup] *******************************************************************
ok: [172.16.200.107]
TASK [install redis] ***********************************************************
ok: [172.16.200.107]
TASK [start redis] *************************************************************
changed: [172.16.200.107]
PLAY RECAP *********************************************************************
172.16.200.107 : ok=3 changed=1 unreachable=0 failed=0
檢測成功后就可以執行了
ansible-playbook test.yml
執行成功的返回結果和測試的一樣,所以就不再顯示了
變量variables:
變量有四種:
1.內建變量
facter -p:收集本機信息,ansible中有個同樣功能的模塊,叫做setup,用ansible-doc -s setup查看這個模塊
用ansible 172.16.200.107 -m setup 就可以收集目標主機的很多信息了,比facter收集到的信息更多,有很多內建的變量,直接就可以使用了.
使用示例:
[root@localhost ansible]# vim /etc/ansible/test1.yml
- hosts: 172.16.200.107
remote_user: root
tasks:
- name: copy file
copy: content={{ ansible_env }} dest=/tmp/ansible_env #{{}}和變量名字之間要加上空格
[root@localhost ansible]# ansible-playbook --syntax-check test1.yml
[root@localhost ansible]# ansible-playbook -C test1.yml
[root@localhost ansible]# ansible-playbook test1.yml
然后到172.16.200.107的/tmp目錄中查看ansible_env文件,看到里邊是本機的各種信息,這個ansible_env就是變量
2.自定義變量
先在文件中定義變量
[root@localhost ansible]# vim /etc/ansible/test2.yml
- hosts: 172.16.200.107
remote_user: root
tasks:
- name: install {{ packgs }} #{{}}和變量名字之間要加上空格
yum: name={{ packgs }} state=latest
然后使用的時候調用定義的變量就可以了
[root@localhost ansible]# ansible-playbook --syntax-check test2.yml
[root@localhost ansible]# ansible-playbook -e packgs=httpd -C test2.yml
[root@localhost ansible]# ansible-playbook -e packgs=httpd test2.yml
3.Host Inventory主機文件變量
就是定義在主機文件中的變量,主機文件默認為/etc/ansible/hosts文件,其格式分為兩種,變量也有兩種,一種可以自定義變量,在模板文件中可以調用,另一種是使用系統本身自帶的invertory參數,這個參數是用于定義ansible遠程連接目標主機時使用的參數,而非傳遞給playbook的變量,常用的有以下幾種:
ansible_ssh_host
ansible_ssh_port
ansible_ssh_user
ansible_ssh_pass
ansbile_sudo_pass
兩種格式:
(a) 單臺主機變量
向不同的主機傳遞不同的變量,在每臺主機之后加上變量=值就可以調用了,格式為
IP/HOSTNAME varaiable=value var2=value2
為了驗證,首先,我們給172.16.200.107上創建用戶feng,密碼設置為123
[root@localhost ansible]# ansible 172.16.200.107 -m user -a "name=feng" #注意,這里使用password=密碼的選項,設置的密碼不是登陸密碼,所以還是要使用下一步這種方式
[root@localhost ansible]# ansible 172.16.200.107 -m shell -a "echo 123|passwd --stdin feng"
然后,修改/etc/ansible/hosts文件,這里我們用invertory參數來演示,自定義變量在說到模板時候在用
[web]
172.16.200.107 ansible_ssh_user=feng ansible_ssh_pass=123
172.16.200.108
測試
[root@localhost ansible]# ansible 172.16.200.107 -m ping
172.16.200.107 | SUCCESS => {
"changed": false,
"ping": "pong"
}
(b)組變量
向組中的主機傳遞相同的變量,就是當組中的每個元素需要設定一個相同的變量時,就可以用這種方式來實現
[groupname:vars]
variable=value
如果107和108兩個主機都需要使用feng用戶登陸,而密碼都是123,那么使用組變量,將之前的設置改一下就可以了,到/etc/ansible/hosts文件中
[web]
172.16.200.107
172.16.200.108
[web:vars]
ansible_ssh_user=feng ansible_ssh_pass=123
這樣,組變量就定義好了,組中的每個元素都使用這個定義的變量
4.roles定義變量
在playbook中,加入如下這一段
vars:
- pbvar: playbook var
就可以直接調用了,這種方式主要是在定義角色中使用,詳細的在說到角色時候在說明
注意:命令行中調用的變量中間有空格會只顯示空格之前的內容
模板templates
ansible-doc -s templates
內嵌一段代碼,由模板引擎解析,可以實現變量替換,算數運算等功能,文件中其他內容不變,這種文件叫做模板文件。他們是用模板模塊生成的,著名的模板模塊叫做python-jinja2,python-jinja2是一個模板引擎,用配置文件后綴為.j2,然后寫的時候用python的語法來寫.
下面我們創建一個顯示本主機ip地址的文件,發給172.16.200.107和108,所以每個主機收到文件內容都是不同的,就實現了模板文件的用法:
root@localhost ansible]# ansible 172.16.200.107 -m setup |less #隨便找一臺主機獲取顯示主機ip的變量名為ansible_ens33.ipv4.address
[root@localhost ansible]# vim /etc/ansible/ip.j2 #定義模板文件,加入如下內容
ipaddress: {{ ansible_ens33.ipv4.address }}
[root@localhost ansible]# vim /etc/ansible/test3.yml #寫playbook文件,注意template模板不能在命令行中被調用
- hosts: all
remote_user: root
tasks:
- name: template test
template: src=/etc/ansible/ip.j2 dest=/tmp/
[root@localhost ansible]# ansible-playbook -C test3.yml
[root@localhost ansible]# ansible-playbook test3.yml
然后到107和108中查看/tmp/ip.j2文件,發現分別為172.16.200.107和172.16.200.108,證明模板引擎已經將變量解析為本機ip了。
條件測試when
可以在tasks中使用,判定條件滿足時候才會執行當前任務,我們用版本號舉例,當107的版本為7時,就執行寫一個centos7字符串到107主機的/tmp/versions文件中,6的話就寫6
[root@localhost ansible]# vim test4.yml
- hosts: 172.16.200.107
remote_user: root
tasks:
- name: test when
copy: content="centos7" dest=/tmp/versions
when: ansible_distribution_major_version == "7"
- name: test when
copy: content="centos6" dest=/tmp/versions
when: ansible_distribution_major_version == "6"
[root@localhost ansible]# ansible-playbook test4.yml #執行
PLAY [172.16.200.107] **********************************************************
TASK [setup] *******************************************************************
ok: [172.16.200.107]
TASK [test when] ***************************************************************
changed: [172.16.200.107]
TASK [test when] ***************************************************************
skipping: [172.16.200.107]
PLAY RECAP *********************************************************************
172.16.200.107 : ok=2 changed=1 unreachable=0 failed=0
我們看到第二個任務skip跳過了,就是因為107是centos7的系統,當檢測不是6的時候,第二個任務就跳過了,我們也可以在107中查看/tmp/versions文件,顯示的為"centos7"
循環迭代with_items
當有很多需要重復執行而任務內容不變的參數時候,重復的寫任務會很麻煩,這個時候需要用到循環,更加高效的來寫參數不同,但是任務過程相同的任務
[root@localhost ansible]# vim /etc/ansible/test5.yml
- hosts: 172.16.200.107
remote_user: root
tasks:
- name: install {{ item }} packges
yum: name={{ item }} state=latest
with_items:
- nginx
- varnish
[root@localhost ansible]# ansible-playbook test5.yml
完成后,我們會看到nginx和varnish都被安裝上了
也可以用字典來迭代:
user: name={{item.name}} group={{item.group}}
with_items:
- {name: 'feng',group:'fengkp'}
- ...
這里就不在列出字典的用法了。
角色
自包含的目錄結構,就是在一個目錄中放入部署這一套服務的各種需要的組件.說白了就是將playbook中的各項組件拆分在各個子文件夾中,由一個主目錄將所有內容包含在其中,這樣做的好處是條理清楚,修改時候比較方便。
目錄結構如下:
- files/ :存放由copy或script模塊等調用的文件;
- templates/:template模塊查找所需要模板文件的目錄;
- tasks/:至少應該包含一個名為main.yml的文件;其它的文件需要在此文件中通過include進行包含;
- handlers/:至少應該包含一個名為main.yml的文件;其它的文件需要在此文件中通過include進行包含;
- vars/:至少應該包含一個名為main.yml的文件;其它的文件需要在此文件中通過include進行包含;
- meta/:至少應該包含一個名為main.yml的文件,定義當前角色的特殊設定及其依賴關系;其它的文件需要在此文件中通過include進行包含;
- default/:設定默認變量時使用此目錄中的main.yml文件;
下面我們來創建一個nginx角色:
mkdir -pv /etc/ansible/roles/nginx/{files,templates,tasks,vars,handlers,meta,default}
[root@localhost ansible]# cd /etc/ansible/roles/nginx/
[root@localhost nginx]# vim tasks/main.yml
- name: install nginx
yum: name=nginx state=latest
when: ansible_os_family == "RedHat"
有一個任務,我們就可以使用了,使用的話在playbook中調用需要使用roles 加上角色名就可以:
[root@localhost nginx]# vim /etc/ansible/nginx.yml
- hosts: 172.16.200.107
remote_user: root
roles:
- nginx
測試
[root@localhost ansible]# ansible-playbook nginx.yml
當然,這里只使用了tasks元素,如果由其他元素的話,就寫在相應的目錄下的main.yml文件中就可以,注意寫腳本的main.yml的文件時候,由于腳本沒有鍵值,所以不是list,不用加前邊的-符號。下面我們通過一個大實驗來熟練ansible的操作
實驗
通過ansible來實現一個架構,架構圖如下:
環境:
- 各個節點時間同步,selinux和iptables關閉
- ansible服務器:172.16.200.109
- 172.16.200.102:部署nginx反代調度器1,部署keepalived,部署varnish服務1
- 172.16.200.103:部署nginx反代調度器2,部署keepalived,部署varnish服務2
- vip:172.16.200.200
- 172.16.200.104:后端nginx節點1,tomcat節點1,mysql主節點,redis主節點
- 172.16.200.105:后端nginx節點2,tomcat節點2,mysql從節點和redis從節點
實現步驟:
我們將結構拆分為靜態和動態兩條,靜態的結構如下:
先來實現靜態這條路線上的nginx,tomcat和redis
1.幾臺主機之間設置ssh密鑰互信
在ansible服務端172.16.200.109執行
[root@localhost ansible]# ssh-keygen -t rsa -P ""
[root@localhost ansible]# for i in {2..5};do ssh-copy-id 172.16.200.10$i;done
分別用ssh登陸上去測試是否已經互信,成功后進入下一步
2.在ansible主機設置/etc/ansible/hosts文件,將主機分組
[director]
172.16.200.10[2:3]
[node]
172.16.200.10[4:5]
3.實現nginx角色,并安裝
在ansible主機上
設置nginx配置文件
[root@localhost ansible]# mkdir -p /etc/ansible/roles/nginx/{files,templates,tasks,vars,handlers,meta,default}
[root@localhost ansible]# cd /etc/ansible/roles/nginx/
[root@localhost nginx]# vim files/nginx_ansible.conf
upstream varnish {
server 172.16.200.102:6081;
server 172.16.200.103:6081;
}
upstream tomcat {
server 172.16.200.104:8080;
server 172.16.200.105:8080;
}
server {
listen 80;
server_name www.feng.com;
location ~ .*\.(js|css|htm|html|gif|jpg|jpeg|png|bmp|swf|ioc|rar|zip|txt|flv|mid|doc|ppt|pdf|xls|mp3|wma)$ {
proxy_pass http://varnish;
}
location ~ \.(jsp|jspx|do|action)(\/.*)?$ {
proxy_pass http://tomcat;
}
}
設置nginx任務文件
[root@localhost nginx]# vim tasks/main.yml
- name: install nginx
yum: name=nginx state=latest
when: ansible_os_family == "RedHat"
- name: delete default.conf
shell: rm -f /etc/nginx/conf.d/default.conf
- name: install nginx config
copy: src=nginx_ansible.conf dest=/etc/nginx/conf.d/
- name: start nginx
service: name=nginx state=started
設置執行nginx的playbook文件
[root@localhost nginx]# vim nginx.yml
- hosts: director
remote_user: root
roles:
- nginx
然后執行安裝
[root@localhost nginx]# ansible-playbook --syntax-check nginx.yml
[root@localhost nginx]# ansible-playbook nginx.yml
PLAY [director] ****************************************************************
TASK [setup] *******************************************************************
ok: [172.16.200.102]
ok: [172.16.200.103]
TASK [nginx : install nginx] ***************************************************
changed: [172.16.200.103]
changed: [172.16.200.102]
TASK [nginx : delete default.conf] *********************************************
changed: [172.16.200.103]
[WARNING]: Consider using file module with state=absent rather than running rm
changed: [172.16.200.102]
TASK [nginx : install nginx config] ********************************************
changed: [172.16.200.103]
changed: [172.16.200.102]
TASK [nginx : start nginx] *****************************************************
changed: [172.16.200.103]
changed: [172.16.200.102]
PLAY RECAP *********************************************************************
172.16.200.102 : ok=5 changed=4 unreachable=0 failed=0
172.16.200.103 : ok=5 changed=4 unreachable=0 failed=0
第一步成功
4.實現tomcat角色并安裝
創建目錄結構并設置任務文件
[root@localhost nginx]# mkdir -p /etc/ansible/roles/tomcat/{files,templates,tasks,vars,handlers,meta,default}
[root@localhost nginx]# cd /etc/ansible/roles/tomcat/
[root@localhost tomcat]# vim tasks/main.yml
- name: install openjdk
yum: name=java-1.8.0-openjdk-devel state=latest
when: ansible_os_family == "RedHat"
- name: install tomcat
yum: name={{ item }} state=latest
with_items:
- tomcat-lib
- tomcat-admin-webapps
- tomcat-docs-webapp
- tomcat-webapps
- name: start tomcat
service: name=tomcat state=started
設置tomcat的playbook文件
[root@localhost tomcat]# vim tomcat.yml
- hosts: node
remote_user: root
roles:
- tomcat
執行安裝
[root@localhost tomcat]# ansible-playbook tomcat.yml
PLAY [node] ********************************************************************
TASK [setup] *******************************************************************
ok: [172.16.200.105]
ok: [172.16.200.104]
TASK [tomcat : install openjdk] ************************************************
changed: [172.16.200.104]
changed: [172.16.200.105]
TASK [tomcat : install tomcat] *************************************************
changed: [172.16.200.105] => (item=[u'tomcat-lib', u'tomcat-admin-webapps', u'tomcat-docs-webapp', u'tomcat-webapps'])
changed: [172.16.200.104] => (item=[u'tomcat-lib', u'tomcat-admin-webapps', u'tomcat-docs-webapp', u'tomcat-webapps'])
TASK [tomcat : start tomcat] ***************************************************
changed: [172.16.200.104]
changed: [172.16.200.105]
PLAY RECAP *********************************************************************
172.16.200.104 : ok=4 changed=3 unreachable=0 failed=0
172.16.200.105 : ok=4 changed=3 unreachable=0 failed=0
成功,可以接著下一步了
5.動態的安裝完成后安裝靜態這條線
首先要安在兩個調度器節點上安裝varnish
創建目錄并設置varnish配置文件
[root@localhost tomcat]# mkdir -p /etc/ansible/roles/varnish/{files,templates,tasks,vars,handlers,meta,default}
[root@localhost tomcat]# cd /etc/ansible/roles/varnish/
[root@localhost varnish]# vim files/default.vcl
import directors;
backend web1 {
.host="172.16.200.104";
.port="80";
}
backend web2 {
.host="172.16.200.105";
.port="80";
}
sub vcl_init {
new server = directors.round_robin();
server.add_backend(web1);
server.add_backend(web2);
}
sub vcl_recv {
if (req.method == "PURGE") {
return(purge);
}
set req.backend_hint = server.backend();
}
sub vcl_pipe {
return (pipe);
}
sub vcl_pass {
return (fetch);
}
設置任務文件
[root@localhost varnish]# vim tasks/main.yml
- name: install varnish
yum: name=varnish state=latest
when: ansible_os_family == "RedHat"
- name: install varnish config
copy: src=default.vcl dest=/etc/varnish
- name: start varnish
service: name=varnish state=started
設置varnish的playbook
- hosts: director
remote_user: root
roles:
- varnish