運維核心工作:
操作系統安裝(物理機、虛擬機)--> 應用程序包部署(安裝、配置、服務啟動 )--> 批量操作 --> 業務系統程序部署(安裝,運行以及發布) --> 監控
-
系統安裝(OS Provisioning):
- bare metal:裸機上安裝系統,pxe預執行環境;cobbler是二次封裝的pxe;
- virtual machine:在虛擬機上安裝系統;
-
Configuration:程序配置
- puppet(ruby語言研發):學習入門曲線陡峭;早先問世,穩定,重量級;
- saltstack(python語言研發):與puppet相似,要有強大的二次研發能力才能填坑,重量級;
- chef:輕量級,使用簡單,早期問世;
- cfengine:
- ansible:
注意:puppet和saltstack都是重量級應用在上百臺服務器以上的運維環境,需要長時間學習才能靈活運用,如果數量較少,支出會大于收益;
這就產生了一些較輕量級、簡單入門學習的運維工具來負責較少量的服務器運維;如chef、cfengine、ansible等;
-
Command and Control:批量執行命令控制
- fabric 輕量級,python語言研發;可編寫fabric腳本完成強大功能;
- func 重量級
- ansible
預發布驗正:
新版本的代碼先發布到服務器(跟線上環境配置完全相同,只是未接入到調度器);程序發布:
不能影響用戶體驗;
系統不能停機;
不能導致系統故障或造成系統完全不可用;灰度發布:
發布路徑:
/webapp/tuangou-1.1
/web/app/tuangou
/webapp/tuangou-1.2
在調度器上下線一批主機(maintanance) --> 關閉服務 --> 部署新版本的應用程序 --> 啟動服務 --> 在調度器上啟用這一批服務器;
- 自動化灰度發布:腳本、發布平臺;
輕量級的運維工具:Ansible
Ansible的特性
- 模塊化:調用特定的模塊,完成特定任務
- 基于Python語言實現,有Paramiko,PyYAML,Jinja2(模板語言)三個關鍵模塊;
- 部署簡單:agentless
- 支持自定義模塊
- 支持playbook編排任務
- 有冪等性:一個任務執行一遍和執行n遍效果一樣,不因為重復執行帶來意外情況
- 安全,基于OpenSSH
- 無需代理不依賴PKI(無需ssl)
- YAML格式編排任務,支持豐富的數據結構
- 較強大的多層解決方案
Ansible的架構
- Core Modules:核心模塊
- Custom Modules:自定義模塊
- Connection Plugins:連接插件
- Host Inventory:ansible管理主機的清單
/etc/ansibe/hosts
- Plugins:模塊功能的補充,如記錄日志發送通知等
- Playbooks 核心組件;任務劇本,編排定義ansible任務集的配置文件,ansible順序依次執行,通常時json格式的yaml文件
Ansible的安裝使用
-
ansible是基于epel倉庫,因此安裝之前先要配置好epel的yum源倉庫
[root@localhost ~]# yum info ansible Loaded plugins: fastestmirror Loading mirror speeds from cached hostfile * extras: ftp.sjtu.edu.cn Available Packages Name : ansible Arch : noarch Version : 2.6.3 Release : 1.el7 Size : 10 M Repo : epel Summary : SSH-based configuration management, deployment, and task execution system URL : http://ansible.com License : GPLv3+ Description : Ansible is a radically simple model-driven configuration management, : multi-node deployment, and remote task execution system. Ansible works : over SSH and does not require any software or daemons to be installed : on remote nodes. Extension modules can be written in any language and : are transferred to managed machines automatically. [root@localhost ~]# rpm -ql ansible |less /etc/ansible/ansible.cfg #ansible主配置文件 /etc/ansible/hosts #主機清單配置文件 /etc/ansible/roles #角色配置文件
ansible的使用方式:
- 在命令行中直接給出
- 在riles中定義好
ansible語法格式:
ansible <host-pattern> [options]
ansible的簡單使用格式:
ansible HOST-PATTERN -m MOD_NAME -a MOD_ARGS -f FORKS -C -u USERNAME -c CONNECTION
-
基于密鑰的方式連接兩臺host主機node1和node2
[root@localhost ~]# ssh-keygen -t rsa -P "" #生成密鑰 [root@localhost ~]# ssh-copy-id -i ~/.ssh/id_rsa.pub root@192.168.43.143 #使用密鑰連接node1 [root@localhost ~]# ssh-copy-id -i ~/.ssh/id_rsa.pub root@192.168.43.129 #使用密鑰連接node2 [root@localhost ~]# vim /etc/ansible/hosts #編輯主機清單文件添加主機 [websrvs] 192.168.43.129 192.168.43.143 [dbsrvs] 192.168.43.129 [root@localhost ~]# ansible all -m ping -C #使用ping命令測試兩臺主機node1,node2;-C:測試模式,干跑; 192.168.43.129 | SUCCESS => { "changed": false, "ping": "pong" } 192.168.43.143 | SUCCESS => { "changed": false, "ping": "pong" }
ansible的常用模塊:
-
group 模塊
[root@localhost ~]# ansible-doc -s group #查看group模塊的幫助文檔 示例: [root@localhost ~]# ansible all -m group -a "gid=3000 name=mygro state=present system=no" 192.168.43.143 | SUCCESS => { "changed": true, "gid": 3000, "name": "mygro", "state": "present", "system": false } 192.168.43.129 | SUCCESS => { "changed": true, "gid": 3000, "name": "mygro", "state": "present", "system": false } #在node1和node2上查看/etc/group文件確認操作是否成功 [root@localhost ~]# tail -1 /etc/group mygro:x:3000:
-
user模塊
*name= 指定要管理的用戶;
state= 為present | absent;
system= 是否創建系統賬號;
uid= 指定UID;
shell= 默認shell類型;
group= 基本組;
groups= 額外(附加)組;
comment= 注釋信息;
home= 用戶的家目錄;
move_home= 移動已存在用戶的家目錄;
password 添加密碼,應該指定的是openssl加密后的密碼;
-
remove 當state=absent時,刪除用戶時同時刪除家目錄;
示例: [root@localhost ~]# ansible all -m user -a "uid=5000 name=testuser state=present groups=mygro shell=/bin/tcsh" 192.168.43.129 | SUCCESS => { "changed": true, "comment": "", "create_home": true, "group": 5000, "groups": "mygro", "home": "/home/testuser", "name": "testuser", "shell": "/bin/tcsh", "state": "present", "system": false, "uid": 5000 } 192.168.43.143 | SUCCESS => { "changed": true, "comment": "", "create_home": true, "group": 5000, "groups": "mygro", "home": "/home/testuser", "name": "testuser", "shell": "/bin/tcsh", "state": "present", "system": false, "uid": 5000 } #在node1和node2上查看用戶創建結果 [root@localhost ~]# tail -1 /etc/passwd testuser:x:5000:5000::/home/testuser:/bin/tcsh
-
copy模塊:`ansible-doc -s copy;用來復制文件到遠程主機
2種用法:- 1)src= dest=
- 2)content= dest=
owner,group,mode 可同時指明文件的屬主、組及權限;
一般有=號的選項為必有選項;
src=為本地文件或目錄;
dest=為遠程被管理主機文件或目錄;
content=表示把此處的內容直接當做源文件;
[root@localhost ~]# ansible all -m copy -a "src=/etc/fstab dest=/tmp/fstab.ansible mode=600"
192.168.43.129 | SUCCESS => {
"changed": true,
"checksum": "413996796bccca42104b6769612d2b57d8085210",
"dest": "/tmp/fstab.ansible",
"gid": 0,
"group": "root",
"md5sum": "95b7fa684cc5066d06f284ce029cddb5",
"mode": "0600",
"owner": "root",
"size": 541,
"src": "/root/.ansible/tmp/ansible-tmp-1536476031.13-129897102565078/source",
"state": "file",
"uid": 0
}
192.168.43.143 | SUCCESS => {
"changed": true,
"checksum": "413996796bccca42104b6769612d2b57d8085210",
"dest": "/tmp/fstab.ansible",
"gid": 0,
"group": "root",
"md5sum": "95b7fa684cc5066d06f284ce029cddb5",
"mode": "0600",
"owner": "root",
"size": 541,
"src": "/root/.ansible/tmp/ansible-tmp-1536476031.16-86269165700494/source",
"state": "file",
"uid": 0
}
node1:
[root@node1 ~]# ll -d /tmp/fstab.ansible
-rw------- 1 root root 541 Sep 9 02:53 /tmp/fstab.ansible
-
command模塊:`ansible-doc -s command;在被管理遠程主機上執行命令;省略模塊時,默認為command模塊;
chdir:指定在哪個目錄下運行命令;
creates:命令運行前創建文件;如果文件存在就不執行命令;
removes:命令運行后移除文件;如果文件不存在就不執行命令;
-
executable:指定shell程序來運行命令;
示例: [root@localhost ~]# ansible all -m command -a "ifconfig" 192.168.43.143 | SUCCESS | rc=0 >> ens33: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500 inet 192.168.43.143 netmask 255.255.255.0 broadcast 192.168.43.255 ... lo: flags=73<UP,LOOPBACK,RUNNING> mtu 65536 inet 127.0.0.1 netmask 255.0.0.0 ... 192.168.43.129 | SUCCESS | rc=0 >> ens33: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500 inet 192.168.43.129 netmask 255.255.255.0 broadcast 192.168.43.255 ... lo: flags=73<UP,LOOPBACK,RUNNING> mtu 65536 inet 127.0.0.1 netmask 255.0.0.0 ... 注意:command執行時不適用shell解析,是裸執行;比如傳遞參數`-a "echo mageedu | passwd --stdin testuser"`不能轉遞密碼給testuser,需要使用下面的shell模塊
-
shell模塊:
ansible-doc -s shell
;在被管理遠程主機上執行命令;但是為調用shell進程,然后把命令在子進程中運行;在執行的命令中可使用管道符;示例: [root@localhost ~]# ansible all -m shell -a "echo mageedu | passwd --stdin testuser" 192.168.43.143 | SUCCESS | rc=0 >> Changing password for user testuser. passwd: all authentication tokens updated successfully. 192.168.43.129 | SUCCESS | rc=0 >> Changing password for user testuser. passwd: all authentication tokens updated successfully.
-
file 模塊:ansible-doc -s file
用法:1)創建鏈接文件:
*path= 指明操作的文件
src= 要鏈接的源文件;
state=link2)修改屬性:
path= owner= mode= group=-
3)創建目錄:
path= state=directory示例: [root@localhost ~]# ansible all -m file -a "path=/var/tmp/hello.dir state=directory" #創建hello.dir目錄 192.168.43.143 | SUCCESS => { "changed": true, "gid": 0, "group": "root", "mode": "0755", "owner": "root", "path": "/var/tmp/hello.dir", "size": 6, "state": "directory", "uid": 0 } 192.168.43.129 | SUCCESS => { "changed": true, "gid": 0, "group": "root", "mode": "0755", "owner": "root", "path": "/var/tmp/hello.dir", "size": 6, "state": "directory", "uid": 0 } node1: [root@node1 ~]# ls -d /var/tmp/hello.* /var/tmp/hello.dir #設定文件屬性 [root@localhost ~]# ansible all -m copy -a "src=/etc/fstab dest=/var/tmp/fstab.ansible" [root@localhost ~]# ansible all -m file -a "src=/var/tmp/fstab.ansible path=/var/tmp/fstab.link state=link" node1: [root@node1 ~]# ll -d /var/tmp/fstab.* -rw-r--r-- 1 root root 541 Sep 9 03:13 /var/tmp/fstab.ansible lrwxrwxrwx 1 root root 22 Sep 9 03:15 /var/tmp/fstab.link -> /var/tmp/fstab.ansible
-
cron模塊:定義任務計劃
minute= 幾分鐘,范圍0-59;
day= 一個月的那一天,范圍1-31,例如,1-5,,/2等
month= 哪個月,范圍1-12;
hour= 哪個小時,范圍0-23;
weekday= 星期幾,范圍0-6;
job= 表示state為present時,要執行的命令;
*name= 必須指定計劃任務條目;
-
state=
present:創建cron計劃任務;默認;
absent:刪除cron計劃任務;示例: [root@localhost ~]# ansible all -m cron -a "minute=*/3 job='/usr/sbin/update 172.16.0.1 &> /dev/null' name=text1" 192.168.43.129 | SUCCESS => { "changed": true, "envs": [], "jobs": [ "text1" ] } 192.168.43.143 | SUCCESS => { "changed": true, "envs": [], "jobs": [ "text1" ] } node1: [root@node1 ~]# crontab -l #Ansible: text1 */3 * * * * /usr/sbin/update 172.16.0.1 &> /dev/null [root@localhost ~]# ansible all -m cron -a "name=text1 state=absent" #刪除定時任務 192.168.43.129 | SUCCESS => { "changed": true, "envs": [], "jobs": [] } 192.168.43.143 | SUCCESS => { "changed": true, "envs": [], "jobs": [] }
-
yum模塊:安裝程序模塊
示例: [root@localhost ~]# ansible all -m yum -a "name=nginx state=installed" 192.168.43.129 | SUCCESS => { "changed": true, "msg": "", "rc": 0, "results": [ ... 192.168.43.143 | SUCCESS => { "changed": true, "msg": "", "rc": 0, "results": [ ... node1: [root@node1 ~]# rpm -q nginx nginx-1.12.2-2.el7.x86_64
-
service 模塊
*name= 指明管理的服務
state=
started 啟動服務;
stopped 停止服務;
restarted 重啟服務;enabled= 開機自動啟動;1或0;
runlevel= 在指定級別下為開機自動啟動;默認為2345,或345級別;
-
arguments 向命令行傳參數;
示例: [root@localhost ~]# ansible all -m service -a "name=nginx state=started" 192.168.43.129 | SUCCESS => { "changed": true, "name": "nginx", "state": "started", "status": { ... 192.168.43.143 | SUCCESS => { "changed": true, "name": "nginx", "state": "started", "status": { ... node1: [root@node1 ~]# ss -tnl LISTEN 0 128 *:80 *:* ...
注意:service有2各選項:enabled設定開機自啟,runlevel在那個級別設定開機自啟
-
spripts模塊:腳本模塊
示例: [root@localhost ~]# vim /tmp/text.sh #!/bin/bash # echo "ansible script" > /tmp/ansible.txt [root@localhost ~]# ansible all -m script -a "/tmp/text.sh" 192.168.43.143 | SUCCESS => { "changed": true, "rc": 0, "stderr": "Shared connection to 192.168.43.143 closed.\r\n", "stderr_lines": [ "Shared connection to 192.168.43.143 closed." ], "stdout": "", "stdout_lines": [] } 192.168.43.129 | SUCCESS => { "changed": true, "rc": 0, "stderr": "Shared connection to 192.168.43.129 closed.\r\n", "stderr_lines": [ "Shared connection to 192.168.43.129 closed." ], "stdout": "", "stdout_lines": [] }
- Playbook的核心元素:
- hosts:主機(關聯到的主機);可以是一個或多個用冒號分隔的主機組;可以是一個主機組,也可以是一個主機;這些主機必須定義在hosts iventory中;
- remoute_user:在遠程主機上執行任務的用戶;即以哪個用戶的身份運行此任務,可以全局指定,也可以在tasks在中單獨指定執行任務的用戶;即不同的任務指明不同的用戶;
- sudo_user:在使用sudo方式時執行任務時,指明臨時切換哪個用戶執行;只是在指明以sudo的方式運行時才使用;
- tasks:任務列表;定義任務的方式主要就是調用模塊和模塊參數;
- variables:變量(多次引用任務使用)
- templates:模板(包含了模板語法的文本文件)
- handlers:從處理器(由特定條件觸發的任務)任務,在特定條件下觸發;在handlers所關注的資源發生改變時才觸發任務;一般使用notify機制通知來觸發;
生效方式:接收到其它任務的通知時被觸發; - roles:角色
playbook的主要作用:
就是能夠把多個相關聯的任務,通過讀取YAML格式的配置文件一次編完;要把任務、變量、模板、處理器放在一個YAML格式文件中進行指定,然后任務就可一次批量執行;
例如:
playbook的基礎組件hosts和tasks演示:
[root@localhost ~]# mkdir -pv playbook
[root@localhost ~]# cd playbook/
[root@localhost playbook]# vim first.yaml
- hosts: all
remote_user: root
tasks:
- name: install redis
yum: name=redis state=latest
- name: start redis
service: name=redis state=started
[root@localhost playbook]# ansible-playbook --check first.yaml
PLAY [all] *************************************************************************************
TASK [Gathering Facts] #只要收集參數成功都顯示ok;*************************************************************************
ok: [192.168.43.143]
ok: [192.168.43.129]
TASK [install redis] #在playbook中定義的第一個任務***************************************************************************
changed: [192.168.43.129]
changed: [192.168.43.143]
TASK [start redis] #在playbook中定義的第二個任務*****************************************************************************
changed: [192.168.43.129]
changed: [192.168.43.143]
PLAY RECAP #返回的報告*************************************************************************************
192.168.43.129 : ok=3 changed=2 unreachable=0 failed=0
192.168.43.143 : ok=3 changed=2 unreachable=0 failed=0
[root@localhost playbook]# ansible-playbook --list-hosts first.yaml
#查看這個playbook運行在哪些主機
playbook: first.yaml
play #1 (all): all TAGS: []
pattern: [u'all']
hosts (2):
192.168.43.143
192.168.43.129
[root@localhost playbook]# ansible-playbook -C first.yaml #干跑一遍測試
[root@localhost playbook]# ansible-playbook first.yaml #真正執行
- 注意:
GATHERING FACTS
第一個任務,是默認的,在每一個目標主機上運行之前,需要知道目標主機的狀態,例如主機名、ip地址等,這些都是內建變量,叫主機的facts變量,是ansible可調用的變量之一;這個過程就是收集變量的過程,也可手動收集; - 如果指明了三個任務,在三臺主機上運行,執行次序是,把第一個任務在第三臺主機運行,沒問題則在三臺主機上再運行第二個任務,如果在運行其中某一主機出現故障,后面的任務會終止;
所以,任務列表,是自上而下,每個任務依次進行的;
指明任務的格式:2種
(1)action: module arguments 較新版本支持
(2)moudule: arguments 所有版本通用 - shell和command模塊參數獨特,后面直接跟命令,而非key=value類的參數列表;
(1)某任務的狀態在運行后為changed時,可通過notify通知給相應的handlers處理器;
(2)任務可以通過tags打標簽,而后可在ansibles-playbook命令上使用-t指定進行調用,可調用多個標簽; - setup模塊:手動收集指定遠程主機的變量
ansible 192.168.43.129 -m setup
示例1:安裝httpd,安裝配置文件,啟動httpd服務
[root@localhost playbook]# mkdir working
[root@localhost playbook]# cd working/
[root@localhost working]# cp /etc/httpd/conf/httpd.conf ./
[root@localhost working]# vim httpd.conf
Listen 8080
[root@localhost playbook]# cd ..
[root@localhost playbook]# vim web.yaml
- hosts: websrvs
remote_user: root
tasks:
- name: install httpd package
yum: name=httpd state=present
- name: install configure file
copy: src=working/httpd.conf dest=/etc/httpd/conf/
- name: start httpd service
service: name=httpd state=started
- name: execute ss command
shell: ss -tnl | grep 8080
[root@localhost playbook]# ansible-playbook --check web.yaml 測試語法
PLAY [websrvs] *********************************************************************************
TASK [Gathering Facts] *************************************************************************
ok: [192.168.43.143]
ok: [192.168.43.129]
TASK [install httpd package] *******************************************************************
changed: [192.168.43.143]
changed: [192.168.43.129]
TASK [install configure file] ******************************************************************
changed: [192.168.43.129]
changed: [192.168.43.143]
TASK [start httpd service] *********************************************************************
changed: [192.168.43.129]
changed: [192.168.43.143]
TASK [execute ss command] **********************************************************************
skipping: [192.168.43.129]
skipping: [192.168.43.143]
PLAY RECAP *************************************************************************************
192.168.43.129 : ok=4 changed=3 unreachable=0 failed=0
192.168.43.143 : ok=4 changed=3 unreachable=0 failed=0
[root@localhost playbook]# ansible-playbook web.yaml #真正執行
注意:在ansible-playbook中執行ss -tnl | grep :8080,這種查詢是不顯示結果的,所以,一般不在ansible-playboot里執行有關查詢顯示的命令;
...
示例2:演示使用handlers,觸發執行;
如果把監聽端改為808,再執行,則不會生效,因為,服務已經啟動了,除非重啟服務,這時,就應該用到handlers處理器
[root@localhost playbook]# vim web-2.yaml
- hosts: websrvs
remote_user: root
tasks:
- name: install httpd package
yum: name=httpd state=present
- name: install configure file
copy: src=working/httpd.conf dest=/etc/httpd/conf/
notify: restart httpd
- name: start httpd service
service: name=httpd state=started
handlers:
- name: restart httpd
service: name=httpd state=restarted
[root@localhost playbook]# vim working/httpd.conf
Listen 808
[root@localhost playbook]# ansible-playbook --check web-2.yaml
[root@localhost playbook]# ansible-playbook web-2.yaml
[root@localhost playbook]# ansible websrvs -m shell -a "ss -tnl | grep 808"
192.168.1.113 | SUCCESS | rc=0 >>
LISTEN 0 128 :::808 :::*
192.168.1.114 | SUCCESS | rc=0 >>
LISTEN 0 128 :::808 :::*
...
示例3:根據上例,如果僅修改了配置文件,卻還要從第一步,執行安裝程序包,這樣是沒必要的,所以,可使用tag,給任務加標簽,不指定標簽時,執行所有任務,加標簽時,只執行標簽所在的任務;
[root@localhost playbook]# vim web-3.yaml
- hosts: websrvs
remote_user: root
tasks:
- name: install httpd package
yum: name=httpd state=present
tags: insthttpd
- name: install configure file
copy: src=working/httpd.conf dest=/etc/httpd/conf/
tags: instconfig
notify: restart httpd
- name: start httpd service
service: name=httpd state=started
tags: starthttpd
handlers:
- name: restart httpd
service: name=httpd state=restarted
[root@localhost playbook]# vim working/httpd.conf
Listen 80
[root@localhost playbook]# ansible-playbook -t insthttpd --check web-3.yaml
[root@localhost playbook]# ansible-playbook -t instconf,insthttpd --check web-3.yaml #調用多個標簽;
8.variables:變量
1)facts:任何facts變量都由正在通信的目標主機發回的信息,ansible自動獲取變量,可直接調用;在setup模塊中查看變量;
2)ansible-playbook命令的命令行中的自定義變量;
-e VARS, --extra-vars=VARS3)通過roles傳遞變量;
4)Host Inventory
- a)向不同的主機傳遞不同的變量;
IP/HOSTNAME varailble=value var2=value2
- b)向組中的主機傳遞相同的變量;
[groupname:vars]
variable=value-
注意:invertory參數:
用于定義ansible遠程路徑目標主機時使用的參數,而非傳遞給playbook的變量;
ansible_ssh_host ansible_ssh_port ansible_ssh_user ansible_ssh_pass ansible_sudo_pass ...
通常ansible中的inventory還有專門參數,不叫變量,因為它不是傳遞給playbook使用的,而是通過ansible連接每個被管理主機時使用的;示例1:演示ansible-playbook命令行調用變量 [root@localhost playbook]# vim forth.yaml - hosts: websrvs remote_user: root tasks: - name: install {{ pkname }} yum: name={{ pkname }} state=present [root@localhost playbook]# ansible-playbook -e pkname=memcached --check forth.yaml [root@localhost playbook]# ansible-playbook -e pkname=memcached forth.yaml
- playbook的其它組件:
變量:5種
ansible facts
可使用setup模塊獲取;收集遠程主機變量;
ansible-playbook -e "var=value"
自定義變量
host variable:host iventory
主機變量
group variable
(主機組上的變量)
roles
-
變量調用方法:{{ variable }}
在playbook中定義變量的方法: vars: - var1: value1 - var2: value2 注意:這種變量有個缺陷,要想改變變量值時都要改變配置文件,不過可在調用時覆蓋其變量的值;
- templates模塊:基于模板方式生成一個文件復制到遠程主機;
*src= 指定本地jinja2的模板文件路徑
*dest= 遠程主機路徑
owner=屬主
group=屬組
mode= 權限
- 模板:templates
就是文本文件,內部嵌套有腳本(這個腳本使用模板編程語言編寫)
python只有在實現web框架時進行嵌入,將自己基于模板編程語言嵌入到其它文本中的機制,它的模板編程語言叫jinja2嵌入式的編程語言;類似于playbook,在python中叫resource資源和清單facts;在清單中定義資源時或定義使用的模板時會用到rubby的模板編程語言;
jinja2模板編程語言所實現的功能是,可以在文本文件中,使用一個所謂的嵌入的標記語法,引入一段模板編程語言所編寫的腳本;而這種腳本無法就是支持比較簡單的編程元素,如條件判斷、(迭代)循環、變量;- jinja2:模板編程語言
字面量:是常見的python對象
字符串:一般使用單引號或雙引號;
數字:整數,浮點數;
列表:使用[item1,tiem2,..],是可變的數據結構;
元組:(item1,item2,...),是不可變的數據結構;
字典:{key1:value1,key2:value2,...},就是鍵值對的組合;
key一般為字符串所以要用引號;
布爾型:true/false- 算術運算:
+,-,,/,//只留商,%只留余數,*
比較操作:
==,!=,>,>=,<,<=
邏輯運算:
and,or,not
- 算術運算:
- jinja2:模板編程語言
演示模板使用:使用ansible在二臺主機上,安裝nginx,提供配置文件,但其中的worker_processores的值要與主機的cpu核心數相同;此時,就可把配置文件基于模板方式提供,而這個worker_processores的值,放的是jinja2所支持的變量,直接使用變量的方式放在那個位置,而本機的template模塊會自動套用這里面變量的值,給ansible facts所報告的結果,并把它生成在這個文件中,而后復制到目標主機上去;這就是模板的作用;
示例:
[root@localhost files]# ansible all -m yum --check -a "name=nginx state=latest" #測試安裝nginx
[root@localhost files]# ansible all -m yum -a "name=nginx state=latest" #安裝nginx
[root@localhost ~]# mkdir files
[root@localhost ~]# cp /etc/nginx/nginx.conf /root/files/nginx.conf.j2
[root@localhost files]# vim nginx.conf.j2
修改:
worker_processes {{ ansible_processor_vcpus }};
[root@localhost files]# vim nginx.yaml
- hosts: websrvs
remote_user: root
tasks:
- name: Install nginx
yum: name=nginx state=present
- name: Install config file
template: src=/root/files/nginx.conf.j2 dest=/etc/nginx/nginx.conf
notify: restart nginx
- name: start service
service: name=nginx state=started
handlers:
- name: restart nginx
service: name=nginx state=restarted
[root@localhost files]# ansible-playbook nginx.yaml --check
[root@localhost files]# ansible-playbook nginx.yaml
可使用主機變量,讓不同主機監聽不同端口:
[root@localhost files]# vim /etc/ansible/hosts
[websrvs]
192.168.255.3 http_port=80 定義主機變量
192.168.255.4 http_port=8080
[root@localhost files]# vim nginx.conf.j2
修改:
listen {{ http_port }}
[root@localhost files]# ansible-playbook nginx.yaml --check
[root@localhost files]# ansible-playbook nginx.yaml
條件測試:when示例
]# scp root@192.168.1.105:/etc/nginx/nginx.conf files/nginx.conf.c6.j2 復制一個centos6上的nginx配置文件;
]# vim files/nginx.conf.c6.j2
worker_processes {{ ansible_processor_vcpus }};
]# vim nginx.yaml
- hosts: all
remote_user: root
tasks:
- name: install nginx
yum: name=nginx state=present
- name: install conf file to c7
template: src=files/nginx.conf.j2 dest=/etc/nginx/nginx.conf
when: ansible_distribution_major_version == "7"
notify: restart nginx
tags: instconf
- name: install conf file to c6
template: src=files/nginx.conf.c6.j2 dest=/etc/nginx/nginx.conf
when: ansible_distribution_major_version == "6"
notify: restart nginx
tags: instconf
- name: start nginx service
service: name=nginx state=started
handlers:
- name: restart nginx
service: name=nginx state=restarted
示例:
同時安裝nginx、memcached、php-fpm等程序包,使用循環
]# vim iter.yaml
- hosts: all
remote_user: root
tasks:
- name: install some packages
yum: name={{ item }} state=present
with_items:
- nginx
- memcached
- php-fpm
- role 角色
有3組服務器web、db、ha都用到時間同步服務,當編寫三個yaml文件分別適用于這3組服務器時,每個文件都要寫一遍時間同步的功能;或另有一種情況,假如第一組服務器即是web又是db,第二組服務器只是db,第三組服務器只是web,此時要寫yaml文件,如果要寫一個db的,再寫一行web的,還要寫一個db和web合并的,如果還要memcached服務器,而有些在db上,有些在web上,在這種場景中,代碼要在不同的主機角色間靈活組合,而這對于此前固化在yaml中的格式顯然是不適用的;
如果把每一種配置的定義的功能獨立化,而且誰用到時誰去調用即可;這種可獨立化的配置通常安照功能為基準進行劃分的;如果服務器安裝了某種功能就扮演成了某種角色;即把db功能的配置定義一個角色,web功能的配置定義一個角色,memcached功能配置定義一個角色等等;需要什么就事先定義好什么,放在特定目錄下,
當主機需要進行配置時,寫一個yaml配置文件,在其里面指明用在哪個主機上、使用remote_user基于哪個運行、調用角色即可;
這就是角色機制,是自包含的,為了讓服務器能夠調用其中的角色實現某種功能,所需要的一切代碼、文件的集合都放在一個特定位置,這個組件就稱為角色;
角色的好處是跟主機是分離的,誰用誰調用;
對于playbook而言,角色就是在playbook中所應該定義各種組件的集合;但此前是寫在playbook一個文件中的,而如果要變成角色,要扮演成一個單獨的目錄;角色名就是目錄名;
每一個角色一般按固定格式定義,任何角色都不能引用自己目錄以外的資源,這樣把這個目錄復制到任何主機上都可以用,這就是自包含應該指明file子目錄;所有的模板放在templates子目錄下;所有的任務放在tasks子目錄下,所有的處理器放在handlers子目錄下;所有變量放在vars子目錄下;還有一個補充meta子目錄;
不是所有目錄必須得有,一般是用到哪些目錄,就給出哪些目錄即可;這就是角色的目錄組織形式;
角色(role);/etc/ansible/roles也可在ansible.cfg中定義;
一般為目錄,每一個角色就是一個子目錄;
角色集合:
roles/
mysql/
httpd/
nginx/
memcached/每個角色,以特定的層級目錄結構進行組織:
mysql/
files/ :存放由copy或script模塊等調用的文件;
templates/ :存放為template模塊查找所需的模板文件目錄;
tasks/ :至少應該包含一個名為main.yml的文件;其它文件需要在此文件中通過include進行包含;
handlers/ :至少應該包含一個名為main.yml的文件;其它文件需要在此文件中通過include進行包含;
vars/ :至少應該包含一個名為main.yml的文件;其它文件需要在此文件中通過include進行包含;
meta/ :定義當前角色的特殊設定及其依賴關系;至少應該包含一個名為main.yml的文件;其它文件需要在此文件中通過include進行包含;
default/ :設定默認變量時使用此目錄中的main.yml文件;-
在playbook調用角色方法1:
- hosts: websrvs remote_user: root roles: - mysql - memcached - nginx
-
在playbook調用角色方法2:在角色調用時,傳遞變量給角色
- hosts: remote_user: roles: - { role: nginx, username: nginx } #鍵role用于指定角色名稱,后續的k/v用于傳遞變量給角色; #還可以基于條件測試實現角色調用; roles: - { role: nginx, when: "ansible_distribution_major_version == '7'" }
Ansible實現主/備模式高可用
-
安裝ansible
[root@localhost ~]# yum -y install ansible keepalive
-
編輯主機清單
[root@localhost ~]# vim /etc/ansible/host [websrvs] 192.168.1.115 192.168.1.116 [hasrvs] 192.168.1.10 192.168.1.11
-
創建固定目錄結構
[root@localhost ~]# mkdir -pv /etc/ansible/roles/{keepalived,nginx}/{files,tasks,templates,handlers,vars,default,meta} [root@localhost ~]# tree /etc/ansible/roles/ /etc/ansible/roles/ ├── keepalived │ ├── default │ ├── files │ ├── handlers │ ├── meta │ ├── tasks │ ├── templates │ └── vars └── nginx ├── default ├── files ├── handlers ├── meta ├── tasks │ └── main.yml ├── templates │ └── index.html.j2 └── vars
3 基于秘鑰連接node1 node2 r1 r2
[root@localhost ~]# ssh-keygen -t rsa -P ""
[root@localhost ~]# ssh-copy-id -i ~/.ssh/id_rsa.pub root@192.168.1.10
[root@localhost ~]# ssh-copy-id -i ~/.ssh/id_rsa.pub root@192.168.1.11
[root@localhost ~]# ssh-copy-id -i ~/.ssh/id_rsa.pub root@192.168.1.115
[root@localhost ~]# ssh-copy-id -i ~/.ssh/id_rsa.pub root@192.168.1.116
4.編輯roles
[root@localhost ~]# vim /etc/ansible/roles/keepalived/tasks/main.yml
#編輯如下內容
- name: install keepalived
yum: name=keepalived state=latest
when: ansible_os_family == "RedHat"
- name: install conf
template: src=kl.conf.j2 dest=/etc/keepalived/keepalived.conf
tags: conf
notify: restart keepalived
- name: start keepalived
service: name=keepalived state=started enabled=yes
[root@localhost ~]# vim /etc/ansible/roles/keepalived/handlers/main.yml
- name: restart keepalived
service: name=keedpalived state=restarted
-
編輯keepalived配置文件,并定義變量
[root@localhost ~]# vim /etc/ansible/roles/keepalived/templates/kl.conf.j2 ! Configuration: command not found global_defs { notification_email { root@localhost } notification_email_from keepalived@localhost smtp_server 127.0.0.1 smtp_connect_timeout 30 router_id {{ ansible_fqdn }} vrrp_mcast_group4 224.1.105.33 } vrrp_instance VI_1 { state {{ kl_status }} interface ens33 virtual_router_id 33 priority {{ kl_priority }} advert_int 1 authentication { auth_type PASS auth_pass XXXX1111 } virtual_ipaddress { 192.168.1.99 dev ens33 label ens33:0 } notify_master "/etc/keepalived/notify.sh master" notify_backup "/etc/keepalived/notify.sh backup" notify_fault "/etc/keepalived/notify.sh fault" } virtual_server 192.168.1.99 80 { delay_loop 1 lb_algo wrr lb_kind DR protocol TCP sorry_server 127.0.0.1 80 real_server 192.168.1.115 80 { weight 1 HTTP_GET { url { path /index.html status_code 200 } nb_get_retry 3 delay_before_retry 2 connect_timeout 3 } } real_server 192.168.1.116 80 { weight 1 HTTP_GET { url { path /index.html status_code 200 } nb_get_retry 3 delay_before_retry 2 connect_timeout 3 } } } [root@localhost files]# vim /etc/ansible/hosts [hasrvs] 192.168.1.10 kl_status=MASTER kl_priority=100 192.168.1.11 kl_status=BACKUP kl_priority=96
-
配置nginx的roles
[root@localhost files]# vim /etc/ansible/roles/nginx/tasks/main.yml - name: Install nginx yum: name=nginx state=latest - name: Install conf template: src=index.html.j2 dest=/usr/share/nginx/html/index.html notify: reload nginx - name: start script script: /root/files/setkl.sh start notify: reload nginx - name: start nginx service: name=nginx state=started [root@localhost files]# vim /etc/ansible/roles/nginx/templates/index.html.j2 <h1> {{ ansible_fqdn }} </h1> [root@localhost files]# vim /etc/ansible/roles/nginx/handlers/main.yml - name: reload nginx service: name=nginx state=reload
7 .編輯keepalived和nginx的playbook
[root@localhost ~]# cd files
[root@localhost files]# vim kl.yml
- hosts: hasrvs
remote_user: root
roles:
- keepalived
[root@localhost files]# vim nginx.yml
- hosts: websrvs
remote_user: root
roles:
- nginx
-
測試并執行
[root@localhost files]# ansible-playbook --check kl.yml [root@localhost files]# ansible-playbook --check kl.yml [root@localhost files]# ansible-playbook --check nginx.yml [root@localhost files]# ansible-playbook nginx.yml
-
訪問測試
[root@localhost files]# curl http://192.168.1.99 <h1> rs1.ilinux.com </h1> [root@localhost files]# curl http://192.168.1.99 <h1> rs2.ilinux.com </h1> [root@localhost files]# curl http://192.168.1.99 <h1> rs1.ilinux.com </h1> [root@localhost files]# curl http://192.168.1.99 <h1> rs2.ilinux.com </h1> [root@localhost files]# curl http://192.168.1.99 <h1> rs1.ilinux.com </h1> node1:規則已生成 [root@node1 ~]# ipvsadm -ln IP Virtual Server version 1.2.1 (size=4096) Prot LocalAddress:Port Scheduler Flags -> RemoteAddress:Port Forward Weight ActiveConn InActConn TCP 192.168.1.99:80 wrr -> 192.168.1.115:80 Route 1 0 3 -> 192.168.1.116:80 Route 1 0 2 [root@node1 ~]# ifconfig ens33: ... ens33:0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500 inet 192.168.1.99 netmask 255.255.255.255 broadcast 0.0.0.0 ether 00:0c:29:6d:e2:f7 txqueuelen 1000 (Ethernet) [root@node1 ~]# systemctl stop keepalived.service node2: [root@node2 ~]# ipvsadm -ln IP Virtual Server version 1.2.1 (size=4096) Prot LocalAddress:Port Scheduler Flags -> RemoteAddress:Port Forward Weight ActiveConn InActConn TCP 192.168.1.99:80 wrr -> 192.168.1.115:80 Route 1 0 0 -> 192.168.1.116:80 Route 1 0 0 #使用客戶端訪問: [root@localhost files]# curl http://192.168.1.99 <h1> rs2.ilinux.com </h1> [root@localhost files]# curl http://192.168.1.99 <h1> rs1.ilinux.com </h1> [root@localhost files]# curl http://192.168.1.99 <h1> rs2.ilinux.com </h1> [root@localhost files]# curl http://192.168.1.99 <h1> rs1.ilinux.com </h1> [root@localhost files]# curl http://192.168.1.99 <h1> rs2.ilinux.com </h1>