Ansible&實(shí)現(xiàn)主/備模式高可用

運(yùn)維核心工作:

操作系統(tǒng)安裝(物理機(jī)、虛擬機(jī))--> 應(yīng)用程序包部署(安裝、配置、服務(wù)啟動)--> 批量操作 --> 業(yè)務(wù)系統(tǒng)程序部署(安裝,運(yùn)行以及發(fā)布)--> 監(jiān)控

  • 系統(tǒng)安裝(OS Provisioning):
    (a) bare metal:裸機(jī)上安裝系統(tǒng),pxe預(yù)執(zhí)行環(huán)境:cobbler是二次封裝的pxe;
    (b) virtual machine:在虛擬機(jī)上安裝系統(tǒng);

  • Configuration:程序配置
    (1) puppet(ruby語言研發(fā)):學(xué)習(xí)入門曲線陡峭;早先問世,穩(wěn)定,重量級;
    (2) saltstack(python語言研發(fā)):與puppet相似,要有強(qiáng)大的二次研發(fā)能力才能填坑,重量級;
    (3) chef:輕量級,使用簡單,早期問世;
    (4) cfengine:
    (5) ansible:

注意:puppet和saltstack都是重量級應(yīng)用在上百臺服務(wù)器以上的運(yùn)維環(huán)境,需要長時(shí)間學(xué)習(xí)才能靈活運(yùn)用,如果數(shù)量較少,支出會大于收益;
這就產(chǎn)生了一些較輕量級、簡單入門學(xué)習(xí)的運(yùn)維工具來負(fù)責(zé)較少量的服務(wù)器運(yùn)維;
如chef、cfengine、ansible等;

  • Command and Control:批量執(zhí)行命令控制
    (1) fabric輕量級,python語言研發(fā);可編寫fabric腳本完成強(qiáng)大功能;
    (2) func 重量級
    (3) ansible

  • 預(yù)發(fā)布驗(yàn)證:
    新版本的代碼先發(fā)布到服務(wù)器(跟線上環(huán)境配置完全相同,只是未接入到調(diào)度器);

  • 程序發(fā)布:
    不能影響用戶體驗(yàn);
    系統(tǒng)不能停機(jī);
    不能導(dǎo)致系統(tǒng)故障或造成系統(tǒng)完全不可用;

  • 灰度發(fā)布:
    發(fā)布路徑:
    /webapp/tuangou-1.1
    /web/app/tuangou
    /webapp/tuangou-1.2

在調(diào)度器上下線一批主機(jī)(maintanance)--> 關(guān)閉服務(wù) --> 部署新版本的應(yīng)用程序 --> 啟動服務(wù) --> 在調(diào)度器上啟用這一批服務(wù)器;

  • 自動化灰度發(fā)布: 腳本、發(fā)布平臺;

輕量級的運(yùn)維工具:Ansible

Ansible的特性
  • 模塊化:調(diào)用特定的模塊,完成特定任務(wù)
  • 基于Python語言實(shí)現(xiàn),有Paramiko,PyYAML,Jinja2(模板語言)三個(gè)關(guān)鍵模塊;
  • 部署簡單:agentless
  • 支持自定義模塊
  • 支持playbook編排任務(wù)
  • 有冪等性:一個(gè)任務(wù)執(zhí)行一遍一執(zhí)行n遍效果不一樣,不因?yàn)橹貜?fù)執(zhí)行帶來意外情況
  • 安全,基于OpenSSH
  • 無需代理不依賴PKI(無需ssl)
  • YAML格式編排任務(wù),支持豐富的數(shù)據(jù)結(jié)構(gòu)
  • 較強(qiáng)大的多層解決方案
Ansible的架構(gòu)
ansible架構(gòu).png
  • Core Modules:核心模塊
  • Custom Modules:自定義模塊
  • Connection Plugins:連接插件
  • Host Inventory:ansible管理主機(jī)的清單/etc/ansible/hosts
  • Plugins:模塊功能的補(bǔ)充,如記錄日志發(fā)送通知等
  • Playbooks 核心組件;任務(wù)劇本,編排定義ansible任務(wù)集的配置文件,ansible順序依次執(zhí)行,通常是json格式的yaml文件
Ansible的安裝使用
  1. ansible是基于epel倉庫,因此安裝之前先要配置epel的yum源倉庫
[root@server1 ~]# yum info ansible
Loaded plugins: fastestmirror, langpacks
Loading mirror speeds from cached hostfile
 * base: mirror.jdcloud.com
 * extras: mirrors.shu.edu.cn
 * updates: mirrors.shu.edu.cn
Available Packages
Name        : ansible
Arch        : noarch
Version     : 2.7.7
Release     : 1.el7
Size        : 11 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@server1 ~]# rpm -ql ansible | less
/etc/ansible/ansible.cfg   #ansible主配置文件
/etc/ansible/hosts         #主機(jī)清單配置文件
/etc/ansible/roles         #角色配置文件
...

  1. ansible的使用方式:
    (1) 在命令行中直接給出
    (2) 在riles中定義好

  2. ansible語法格式: ansible <host-pattern> [options]

  3. ansible的簡單格式:ansible HOST-PATTERN -m MOD_NAME -a MOD_ARGS -f FORKS -C -u USERNAME -c CONNECTION

  4. 基于密鑰的方式連接兩臺host主機(jī)node1和node2

[root@server1 ~]# ssh-keygen -t rsa -P ""    #生成密鑰
[root@server1 ~]# ssh-copy-id -i ~/.ssh/id_rsa.pub root@192.168.1.128
    # 使用密鑰連接node1
[root@server1 ~]# ssh-copy-id -i ~/.ssh/id_rsa.pub root@192.168.1.129
    # 使用密鑰連接node2

[root@server1 ~]# vim /etc/ansible/hosts    # 編輯主機(jī)清單文件添加主機(jī)
    [websrvs]
    192.168.1.128
    192.168.1.129

    [dbsrvs]
    192.168.1.128

[root@server1 ~]# ansible all -m ping -C    # 使用ping命令測試兩臺主機(jī)node1,node2;-C:測試模式,干跑;
192.168.1.129 | SUCCESS => {
    "changed": false, 
    "ping": "pong"
}
192.168.1.128 | SUCCESS => {
    "changed": false, 
    "ping": "pong"
}

  1. ansible的常用模塊:
    • group模塊
    [root@server1 ~]# ansible-doc -s group    # 查看group模塊的幫助文檔
    示例:
    [root@server1 ~]# ansible all -m group -a "gid=3000 name=mygro state=present system=no"
    192.168.1.129 | CHANGED => {
        "changed": true, 
        "gid": 3000, 
        "name": "mygro", 
        "state": "present", 
        "system": false
    }
    192.168.1.128 | CHANGED => {
        "changed": true, 
        "gid": 3000, 
        "name": "mygro", 
        "state": "present", 
        "system": false
    }
    # 在node1和node2上查看/etc/group文件確認(rèn)操作是否成功
    [root@node1 ~]# tail -1 /etc/group
    mygro:x:3000:
    
    
    • user模塊
      • *name= 指定要管理的用戶;
      • state= 為present | absent;
      • system= 是否創(chuàng)建系統(tǒng)帳號;
      • uid= 指定UID;
      • shell= 默認(rèn)shell類型;
      • group= 基于組;
      • groups= 額外(附加)組;
      • comment= 注釋信息;
      • home= 用戶的家目錄;
      • move_home= 移動已存在用戶的家目錄;
      • password 添加密碼,應(yīng)該指定是的openssl加密后的密碼;
      • remove 當(dāng)state=absent時(shí),刪除用戶時(shí)同時(shí)刪除家目錄;
示例:
[root@server1 ~]# ansible all -m user -a "uid=5000 name=testuser state=present groups=mygro shell=/bin/tcsh"
192.168.1.128 | CHANGED => {
    "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.1.129 | CHANGED => {
    "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上查看用戶創(chuàng)建結(jié)果
[root@node1 ~]# tail -1 /etc/passwd
testuser:x:5000:5000::/home/testuser:/bin/tcsh

  • copy模塊:ansible-doc -s copy;用來復(fù)制文件到遠(yuǎn)程主機(jī)
    2種用法:
    1)src= dest=
    2)content= dest=
    owner,group,mode可同時(shí)指明文件的屬主、組及權(quán)限;
    一般有=號的選項(xiàng)為必有選項(xiàng);
    src=為本地文件或目錄;
    dest=為遠(yuǎn)程被管理主機(jī)文件或目錄;
    content=表示把此處的內(nèi)空直接當(dāng)做源文件;
[root@server1 ~]# ansible all -m copy -a "src=/etc/fstab dest=/tmp/fstab.ansible mode=600"

192.168.1.128 | CHANGED => {
    "changed": true, 
    "checksum": "f5dec7037c1be2d9c54110114ffb00b9efb834ba", 
    "dest": "/tmp/fstab.ansible", 
    "gid": 0, 
    "group": "root", 
    "md5sum": "03ef74ad995b394c5265817e23ee086e", 
    "mode": "0600", 
    "owner": "root", 
    "secontext": "unconfined_u:object_r:admin_home_t:s0", 
    "size": 541, 
    "src": "/root/.ansible/tmp/ansible-tmp-1550649775.34-194820899443510/source", 
    "state": "file", 
    "uid": 0
}
192.168.1.129 | CHANGED => {
    "changed": true, 
    "checksum": "f5dec7037c1be2d9c54110114ffb00b9efb834ba", 
    "dest": "/tmp/fstab.ansible", 
    "gid": 0, 
    "group": "root", 
    "md5sum": "03ef74ad995b394c5265817e23ee086e", 
    "mode": "0600", 
    "owner": "root", 
    "secontext": "unconfined_u:object_r:admin_home_t:s0", 
    "size": 541, 
    "src": "/root/.ansible/tmp/ansible-tmp-1550649775.36-53381265559986/source", 
    "state": "file", 
    "uid": 0
}

# 在node1和node2上查看結(jié)果
[root@node1 ~]# ll -d /tmp/fstab.ansible 
-rw-------. 1 root root 541 Feb 20 16:02 /tmp/fstab.ansible

  • command模塊:ansible-doc -s command;在被管理遠(yuǎn)程主機(jī)上執(zhí)行命令;省略模塊時(shí),默認(rèn)為command模塊;
    • chdir:指定在哪個(gè)目錄下運(yùn)行命令;
    • creates:命令運(yùn)行前創(chuàng)建文件;如果文件存在就不執(zhí)行命令;
    • removes:命令運(yùn)行后移除文件;如果文件不存在就不執(zhí)行命令;
    • executable:指定shell程序來運(yùn)行命令;
示例:
[root@server1 ~]# ansible all -m command -a "ifconfig"
192.168.1.129 | CHANGED | rc=0 >>
ens33: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 192.168.1.129  netmask 255.255.255.0  broadcast 192.168.1.255
        ...
lo: flags=73<UP,LOOPBACK,RUNNING>  mtu 65536
        inet 127.0.0.1  netmask 255.0.0.0
        ...
virbr0: flags=4099<UP,BROADCAST,MULTICAST>  mtu 1500
        inet 192.168.122.1  netmask 255.255.255.0  broadcast 192.168.122.255
        ...
192.168.1.128 | CHANGED | rc=0 >>
ens33: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 192.168.1.128  netmask 255.255.255.0  broadcast 192.168.1.255
        ...
lo: flags=73<UP,LOOPBACK,RUNNING>  mtu 65536
        inet 127.0.0.1  netmask 255.0.0.0
        ...
virbr0: flags=4099<UP,BROADCAST,MULTICAST>  mtu 1500
        inet 192.168.122.1  netmask 255.255.255.0  broadcast 192.168.122.255 
        ...

注意:command執(zhí)行時(shí)不適用shell解析,是裸執(zhí)行;比如傳遞參數(shù)`-a "echo mageedu | passwd --stdin testuser"`不能傳遞密碼給testuser,需要使用下面的shell模塊

  • shell模塊:ansible-doc -s shell;在被管理遠(yuǎn)程主機(jī)上執(zhí)行命令;但是為調(diào)用shell進(jìn)程,然后把命令在子進(jìn)程中運(yùn)行;在執(zhí)行的命令中可使用管道符;
示例:
[root@server1 ~]# ansible all -m shell -a "echo mageedu | passwd --stdin testuser"
192.168.1.128 | CHANGED | rc=0 >>
Changing password for user testuser.
passwd: all authentication tokens updated successfully.

192.168.1.129 | CHANGED | rc=0 >>
Changing password for user testuser.
passwd: all authentication tokens updated successfully.

  • file 模塊:ansible-doc -s file
    用法:
    • 創(chuàng)建鏈接文件:
      path= 指明操作的文件
      src= 要鏈接的源文件;
      state= link
    • 修改屬性:
      path=
      owner=
      mode=
      group=
    • 創(chuàng)建目錄:
      path=
      state= directory
示例:
[root@server1 ~]# ansible all -m file -a "path=/var/tmp/hello.dir state=directory" 
192.168.1.128 | CHANGED => {
    "changed": true, 
    "gid": 0, 
    "group": "root", 
    "mode": "0755", 
    "owner": "root", 
    "path": "/var/tmp/hello.dir", 
    "secontext": "unconfined_u:object_r:user_tmp_t:s0", 
    "size": 6, 
    "state": "directory", 
    "uid": 0
}
192.168.1.129 | CHANGED => {
    "changed": true, 
    "gid": 0, 
    "group": "root", 
    "mode": "0755", 
    "owner": "root", 
    "path": "/var/tmp/hello.dir", 
    "secontext": "unconfined_u:object_r:user_tmp_t:s0", 
    "size": 6, 
    "state": "directory", 
    "uid": 0
}

node1:
[root@node1 ~]# ls -d /var/tmp/hello.*
/var/tmp/hello.dir

#設(shè)定文件屬性
[root@server1 ~]# ansible all -m copy -a "src=/etc/fstab dest=/var/tmp/fstab.ansible"
[root@server1 ~]# ansible all -m file -a "src=/var/tmp/fstab.ansible path=/var/tmp/fstab.link state=link"
192.168.1.128 | CHANGED => {
    "changed": true, 
    "dest": "/var/tmp/fstab.link", 
    "gid": 0, 
    "group": "root", 
    "mode": "0777", 
    "owner": "root", 
    "secontext": "unconfined_u:object_r:user_tmp_t:s0", 
    "size": 22, 
    "src": "/var/tmp/fstab.ansible", 
    "state": "link", 
    "uid": 0
}
192.168.1.129 | CHANGED => {
    "changed": true, 
    "dest": "/var/tmp/fstab.link", 
    "gid": 0, 
    "group": "root", 
    "mode": "0777", 
    "owner": "root", 
    "secontext": "unconfined_u:object_r:user_tmp_t:s0", 
    "size": 22, 
    "src": "/var/tmp/fstab.ansible", 
    "state": "link", 
    "uid": 0
}

node1:
[root@node1 ~]# ll -d /var/tmp/fstab.*
-rw-r--r--. 1 root root 541 Feb 20 16:25 /var/tmp/fstab.ansible
lrwxrwxrwx. 1 root root  22 Feb 20 16:30 /var/tmp/fstab.link -> /var/tmp/fstab.ansible

  • cron模塊:定義任務(wù)計(jì)劃
    • minute= 幾分鐘,范圍0-59
    • day= 一個(gè)月的那一天,范圍1-31,例如:1-5,/2等
    • month= 哪個(gè)月,范圍1-12;
    • hour= 哪個(gè)小時(shí),范圍0-23;
    • weekday= 星期幾,范圍0-6;
    • job= 表示 state為present時(shí),要執(zhí)行的命令;
    • name= 必須指定計(jì)劃任務(wù)條目;
    • state=
      present:創(chuàng)建cron計(jì)劃任務(wù);默認(rèn);
      absent:刪除cron計(jì)劃任務(wù);
示例:
[root@server1 ~]# ansible all -m cron -a "minute=*/3 job='/usr/sbin/update 192.168.1.254 &> /dev/null' name=text1"
192.168.1.128 | CHANGED => {
    "changed": true, 
    "envs": [], 
    "jobs": [
        "text1"
    ]
}
192.168.1.129 | CHANGED => {
    "changed": true, 
    "envs": [], 
    "jobs": [
        "text1"
    ]
}

node1:
[root@node1 ~]# crontab -l
#Ansible: text1
*/3 * * * * /usr/sbin/update 192.168.1.254 &> /dev/null

[root@server1 ~]# ansible all -m cron -a "name=text1 state=absent"    #刪除定時(shí)任務(wù)
192.168.1.129 | CHANGED => {
    "changed": true, 
    "envs": [], 
    "jobs": []
}
192.168.1.128 | CHANGED => {
    "changed": true, 
    "envs": [], 
    "jobs": []
}

  • yum模塊:安裝程序模塊
[root@server1 ~]# ansible all -m yum -a "name=nginx state=installed"
192.168.1.128 | SUCCESS => {
      "changed": true, 
      "msg": "", 
      "rc": 0, 
      "results": [
      ...
  192.168.1.129 | SUCCESS => {
      "changed": true, 
      "msg": "", 
      "rc": 0, 
      "results": [
      ...
node1:
[root@node1 ~]# rpm -q nginx
nginx-1.12.2-2.el7.x86_64

  • service模塊:
    • name= 指明管理的服務(wù)
    • state=
      started 啟動服務(wù);
      stopped 停止服務(wù);
      restarted重啟服務(wù);
    • enabled= 開機(jī)自動啟動;1或0
    • runlevel= 在指定級別下為開機(jī)自動啟動;默認(rèn)為2345,或345級別;
    • arguments 向命令行傳參數(shù);
示例:
[root@server1 ~]# ansible all -m service -a "name=nginx state=started"
192.168.1.128 | CHANGED => {
    "changed": true, 
    "name": "nginx", 
    "state": "started", 
    "status": {
    ...
192.168.1.129 | CHANGED => {
    "changed": true, 
    "name": "nginx", 
    "state": "started", 
    "status": {
    ...

node1:
[root@node1 ~]# ss -tnl
State       Recv-Q Send-Q            Local Address:Port                           Peer Address:Port                             
LISTEN      0      128                           *:80                                        *:*    
... 

注意:service有2個(gè)選項(xiàng):
    enabled設(shè)定開機(jī)自動啟動
    runlevel在哪個(gè)級別設(shè)定開機(jī)自動啟動
  • spripts模塊:腳本模塊
示例:
[root@server1 ~]# vim /tmp/text.sh
    #!/bin/bash
    #
    echo "ansible script" > /tmp/ansible.txt

[root@server1 ~]# ansible all -m script -a "/tmp/text.sh"
192.168.1.129 | CHANGED => {
    "changed": true, 
    "rc": 0, 
    "stderr": "Shared connection to 192.168.1.129 closed.\r\n", 
    "stderr_lines": [
        "Shared connection to 192.168.1.129 closed."
    ], 
    "stdout": "", 
    "stdout_lines": []
}
192.168.1.128 | CHANGED => {
    "changed": true, 
    "rc": 0, 
    "stderr": "Shared connection to 192.168.1.128 closed.\r\n", 
    "stderr_lines": [
        "Shared connection to 192.168.1.128 closed."
    ], 
    "stdout": "", 
    "stdout_lines": []
}

  1. Playbook的核心元素:
  • hosts:主機(jī)(關(guān)聯(lián)到的主機(jī));可以是一個(gè)或多個(gè)用冒號分隔的主機(jī)組;可以是一個(gè)主機(jī)組,也可以是一個(gè)主機(jī);這些主機(jī)必須定義在hosts iventory中;
    - remoute_user:在遠(yuǎn)程主機(jī)上執(zhí)行任務(wù)的用戶;即以哪個(gè)用戶的身份運(yùn)行此任務(wù),可以全局指定,也可以在tasks中單獨(dú)指定執(zhí)行任務(wù)的用戶;即不同的任務(wù)指明不同的用戶;
    - sudo_user:在使用sudo方式時(shí)執(zhí)行任務(wù)時(shí),指明臨時(shí)切換哪個(gè)用戶執(zhí)行;只是在指明以sudo的方式運(yùn)行時(shí)才使用;
    • tasks:任務(wù)列表;定義任務(wù)的方式主要就是調(diào)用模塊和模塊參數(shù);
    • variables:變量(多次引用任務(wù)使用)
    • templates:模板(包含了模板語法的文本文件)
    • handlers:從處理器(由特定條件觸發(fā)的任務(wù))任務(wù),在特定條件下觸發(fā);在handlers所關(guān)注的資源發(fā)生改變時(shí)才觸發(fā)任務(wù);一般使用notify機(jī)制通知來觸發(fā);
      生效方式:接收到其他任務(wù)的通知時(shí)被觸發(fā);
    • roles:角色

playbook的主要作用:
就是能夠把多個(gè)相關(guān)聯(lián)的任務(wù),通過讀取YAML格式的配置文件一次編完;要把任務(wù)、變量、模板、處理器放在一個(gè)YAML格式文件中進(jìn)行指定,然后任務(wù)就可一次批量執(zhí)行;

例如:
playbook的基礎(chǔ)組件hosts和tasks演示:
[root@server1 ~]# mkdir playbook
[root@server1 ~]# cd playbook/
[root@server1 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@server1 playbook]# ansible-playbook --check first.yaml 

PLAY [all] ********************************************************************************************************

TASK [Gathering Facts]  #只要收集參數(shù)成功都顯示ok; ********************************************************************************************
ok: [192.168.1.128]
ok: [192.168.1.129]

TASK [install redis]  #在playbook中定義的第一個(gè)任務(wù)**********************************************************************************************
changed: [192.168.1.128]
changed: [192.168.1.129]

TASK [start redis]   #在playbook中定義的第二個(gè)任務(wù)************************************************************************************************
changed: [192.168.1.129]
changed: [192.168.1.128]

PLAY RECAP   #返回的報(bào)告********************************************************************************************************
192.168.1.128              : ok=3    changed=2    unreachable=0    failed=0   
192.168.1.129              : ok=3    changed=2    unreachable=0    failed=0

[root@server1 playbook]# ansible-playbook --list-hosts first.yaml
#查看這個(gè)playbook運(yùn)行在哪些主機(jī)

playbook: first.yaml

  play #1 (all): all    TAGS: []
    pattern: [u'all']
    hosts (2):
      192.168.1.128
      192.168.1.129

[root@server1 playbook]# ansible-playbook -C first.yaml   #干跑一遍測試
[root@server1 playbook]# ansible-playbook first.yaml  #真正執(zhí)行
  • 注意:GATHERING FACTS第一個(gè)任務(wù),是默認(rèn)的,在每一個(gè)目標(biāo)主機(jī)上運(yùn)行之前,需要知道目標(biāo)主機(jī)的狀態(tài),例如主機(jī)名、ip地址等,這些都是內(nèi)建變量,叫主機(jī)的facts變量,是ansible可調(diào)用的變量之一;這個(gè)過程就是收集變量的過程,也可以手動收集;
  • 如果指明了三個(gè)任務(wù),在三臺主機(jī)上運(yùn)行,執(zhí)行次序是,把第一個(gè)任務(wù)在三臺主機(jī)運(yùn)行,沒問題則在三臺主機(jī)上再運(yùn)行第二個(gè)任務(wù),如果在運(yùn)行其中某一主機(jī)出現(xiàn)故障,后面的任務(wù)會終止;
    所以,任務(wù)列表,是自上而下,每個(gè)任務(wù)依次進(jìn)行的;
    指明任務(wù)的格式:2種
    (1) action:module arguments 較新版本支持
    (2) module:arguments 所有版本通用
  • shell和command模塊參數(shù)獨(dú)特,后面直接跟命令,而非key=value類的參數(shù)列表;
    (1) 某任務(wù)的狀態(tài)在運(yùn)行后為changed時(shí),可通過notify通知給相應(yīng)的handlers處理器;
    (2) 任務(wù)可以通過tags打標(biāo)簽,而后可在ansibles-playbook命令上使用-t指定進(jìn)行調(diào)用,可調(diào)用多個(gè)標(biāo)簽;
  • setup模塊:手動收集指定遠(yuǎn)程主機(jī)的變量
    ansible 192.168.1.128 -m setup
示例1:安裝httpd,安裝配置文件,啟動httpd服務(wù)
[root@server1 playbook]# mkdir working
[root@server1 playbook]# cd working/
[root@server1 working]# cp /etc/httpd/conf/httpd.conf ./
[root@server1 working]# vim httpd.conf
    Listen 8080
[root@server1 working]# cd ..
[root@server1 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: excute ss command
        shell: ss -tnl | grep 8080

[root@server1 playbook]# ansible-playbook --check web.yaml  #測試語法 
[root@server1 playbook]# ansible-playbook web.yaml  #真正執(zhí)行

注意:在ansible-playbook中執(zhí)行ss -tnl | grep  :8080,這種查詢是不顯示,所以,一般不在ansible-playbook里執(zhí)行有關(guān)查詢顯示的命令;

示例2:演示使用handlers,觸發(fā)執(zhí)行;
如果把監(jiān)聽端改為808,再執(zhí)行,則不會生效,因?yàn)?,服?wù)已經(jīng)啟動了,除非重啟服務(wù),這時(shí),就應(yīng)該用到handlers處理器
[root@server1 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/
      - name: start httpd service
        service: name=httpd state=started
      handlers:
      - name: restart httpd
        service: name=httpd state=restarted

[root@server1 playbook]# vim working/httpd.conf 
    Listen 808
[root@server1 playbook]# ansible-playbook --check web-2.yaml 
[root@server1 playbook]# ansible-playbook web-2.yaml 
[root@server1 playbook]# ansible websrvs -m shell -a "ss -tnl | grep 808"
192.168.1.129 | CHANGED | rc=0 >>
LISTEN     0      128         :::808                    :::*                  

192.168.1.128 | CHANGED | rc=0 >>
LISTEN     0      128         :::808                    :::* 
示例3:
根據(jù)上例,如果僅修改了配置文件,卻還要從第一步,執(zhí)行安裝程序包,這樣是沒必要的,所以,可使用tag,給任務(wù)加標(biāo)簽,不指定標(biāo)簽時(shí),執(zhí)行所有任務(wù),加標(biāo)簽時(shí),只執(zhí)行標(biāo)簽所在的任務(wù);
[root@server1 playbook]# cp web-2.yaml web-3.yaml
[root@server1 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: instconf
  - name: start httpd service
    service: name=httpd state=started
    tags: starthttpd
  handlers:
  - name: restart httpd
    service: name=httpd state=restarted
[root@server1 playbook]# vim working/httpd.conf
    Listen 80
[root@server1 playbook]# ansible-playbook -t insthttpd --check web-3.yaml
[root@server1 playbook]# ansible-playbook -t instconf,insthttpd --check web-3.yaml   #調(diào)用多個(gè)標(biāo)簽;

  1. variables:變量
    • 1) facts:任何facts變量都由正在通信的目標(biāo)主機(jī)發(fā)回的信息,ansible自動獲取變量,可直接調(diào)用;在setup模塊中查看變量;
    • 2)ansible-playbook命令的命令行中的自定義變量;
      -e VARS, --extra-vars=VARS
    • 3)通過roles傳遞變量;
    • 4)Host Inventory
      • a)向不同的主機(jī)傳遞不同的變量;
      • b)向組中的主機(jī)傳遞相同的變量;
        [groupname:vars]
        variable=value
    • 注意:invertory參數(shù):
      用于定義ansible遠(yuǎn)程路徑目標(biāo)主機(jī)時(shí)使用的參數(shù),而非傳遞給playbook的變量;
      ansible_ssh_host
      ansible_ssh_port
      ansible_ssh_user
      ansible_ssh_pass
      ansible_sudo_pass
      ...
      通常ansible中的inventory還有專門參數(shù),不叫變量,因?yàn)樗皇莻鬟f給playbook使用的,而是通過ansible連接每個(gè)被管理主機(jī)時(shí)使用的;
示例1:演示ansible-playbook命令行調(diào)用變量
[root@server1 playbook]# vim forth.yaml
    - hosts: websrvs
      remote_user: root
      tasks:
      - name: install {{ pkname }}
        yum: name={{ pkname }} state=present
[root@server1 playbook]# ansible-playbook -e pkname=memcached --check forth.yaml
[root@server1 playbook]# ansible-playbook -e pkname=memcached forth.yaml

  1. playbook的其它組件:
    • 變量:5種
      ansible facts 可使用setup模塊獲?。皇占h(yuǎn)程主機(jī)變量;
      ansible-playbook -e "var=value" 自定義變量
      host variable:host iventory 主機(jī)變量
      group variable (主機(jī)組上的變量)
      roles
    • 變量調(diào)用方法:{{ variable }}
在playbook中定義變量的方法:
    vars:
    - var1: value1
    - var2: valure2
    注意:這種變量有個(gè)缺陷,要想改變變量值時(shí)都要改變配置文件,不過可在調(diào)用時(shí)覆蓋其變量的值;

  1. templates模塊:基于模板方式生成一個(gè)文件復(fù)制到遠(yuǎn)程主機(jī);
    src= 指定本地jinja2的模板文件路徑
    dest= 遠(yuǎn)程主機(jī)路徑
    owner= 屬主
    group= 屬組
    mode= 權(quán)限
    • 模板:templates
      就是文本文件,內(nèi)部嵌套有腳本(這個(gè)腳本使用模板編程語言編寫)
      python只有在實(shí)現(xiàn)web框架時(shí)進(jìn)行嵌入,將自己基于模板編程語言嵌入到其它文本中的機(jī)制,它的模板編程語言叫jinja2嵌入式的編程語言;類似于playbook,在python中叫resource資源和清單facts;在清單中定義資源時(shí)或定義使用的模板時(shí)會用到rubby的模板編程語言;
      jinja2模板編程語言所實(shí)現(xiàn)的功能是,可以在文本文件中,使用一個(gè)所謂的嵌入的標(biāo)記語法,引入一段模板編程語言所編寫的腳本;而這種腳本無法就是支持比較簡單的編程元素,如條件判斷、(迭代)循環(huán)、變量;

      • jinja2:模板編程語言
        字面量:是常見的python對象
        字符串:一般使用單引號或雙引號;
        數(shù)字:整數(shù),浮點(diǎn)數(shù);
        列表:使用[item1,tiem2,..],是可變的數(shù)據(jù)結(jié)構(gòu);
        元組:(item1,item2,...),是不可變的數(shù)據(jù)結(jié)構(gòu);
        字典:{key1:value1,key2:value2,...},就是鍵值對的組合;
        key一般為字符串所以要用引號;
        布爾型:true/false
        • 算術(shù)運(yùn)算:
          +,-,,/, //只留商,%只留余數(shù),*
          比較操作:
          ==, !=, >, >=, <, <=
          邏輯運(yùn)算:
          and,or,not

演示模板使用:使用ansible在二臺主機(jī)上,安裝nginx,提供配置文件,但其中的worker_processores的值要與主機(jī)的cpu核心數(shù)相同;此時(shí),就可把配置文件基于模板方式提供,而這個(gè)worker_processores的值,放的是jinja2所支持的變量,直接使用變量的方式放在那個(gè)位置,而本機(jī)的template模塊會自動套用這里面變量的值,給ansible facts所報(bào)告的結(jié)果,并把它生成在這個(gè)文件中,而后復(fù)制到目標(biāo)主機(jī)上去;這就是模板的作用;

示例:
[root@server1 ~]# ansible all -m yum --check -a "name=nginx state=latest"  #測試安裝nginx
[root@server1 ~]# ansible all -m yum -a "name=nginx state=latest"  #安裝nginx
[root@server1 ~]# mkdir files
[root@server1 ~]# cp /etc/nginx/nginx.conf /root/files/nginx.conf.j2
[root@server1 ~]# cd files
[root@server1 files]# vim nginx.conf.j2
修改:
    worker_processes {{ ansible_processor_vcpus }};

[root@server1 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@server1 files]# ansible-playbook nginx.yaml --check
[root@server1 files]# ansible-playbook nginx.yaml

可使用主機(jī)變量,讓不同主機(jī)監(jiān)聽不同端口:
[root@server1 files]# vim /etc/ansible/hosts
    [websrvs]
    192.168.1.128 http_port=80  #定義主機(jī)變量
    192.168.1.129 http_port=8080
[root@server1 files]# vim nginx.conf.j2
修改:
    listen       {{ http_port }};
[root@server1 files]# ansible-playbook nginx.yaml --check
[root@server1 files]# ansible-playbook nginx.yaml 

條件測試:when示例
    ]# scp root@192.168.1.105:/etc/nginx/nginx.conf files/nginx.conf.c6.j2 復(fù)制一個(gè)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

    示例:
    同時(shí)安裝nginx、memcached、php-fpm等程序包,使用循環(huán)
    ]# vim iter.yaml
    - hosts: all
      remote_user: root
      tasks:
      - name: install some packages
        yum: name={{ item }} state=present
        with_items:
        - nginx
        - memcached
        - php-fpm

  1. role 角色

    有3組服務(wù)器web、db、ha都用到時(shí)間同步服務(wù),當(dāng)編寫三個(gè)yaml文件分別適用于這3組服務(wù)器時(shí),每個(gè)文件都要寫一遍時(shí)間同步的功能;或另有一種情況,假如第一組服務(wù)器即是web又是db,第二組服務(wù)器只是db,第三組服務(wù)器只是web,此時(shí)要寫yaml文件,如果要寫一個(gè)db的,再寫一行web的,還要寫一個(gè)db和web合并的,如果還要memcached服務(wù)器,而有些在db上,有些在web上,在這種場景中,代碼在不同的主機(jī)角色間靈活組合,而這對于此前固化在yaml中的格式顯然是不適用的;
    如果把每一種配置的定義的功能獨(dú)立化,而且誰用到時(shí)誰去調(diào)用即可;這種可獨(dú)立化的配置通常按照功能為基準(zhǔn)進(jìn)行劃分的;如果服務(wù)器安裝了某種功能就扮演成了某種角色;即把db功能的定義一個(gè)角色,web功能的配置定義一個(gè)角色,memcached功能配置定義一個(gè)角色等等;需要什么就事先定義好什么,放在特定目錄下,當(dāng)主機(jī)需要進(jìn)行配置時(shí),寫一個(gè)yaml配置文件,在其里面指明用在哪個(gè)主機(jī)上、使用remote_user基于哪個(gè)運(yùn)行、調(diào)用角色即可;

這就是角色機(jī)制,是自包含的,為了讓服務(wù)器能夠調(diào)用其中的角色實(shí)現(xiàn)某種功能,所需要的一切代碼、文件的集合都放在一個(gè)特定位置,這個(gè)組件就稱為角色;
角色的好處是跟主機(jī)是分離的,誰用誰調(diào)用;
對于playbook而言,角色就是在playbook中所應(yīng)該定義各種組件的集合;但此前是寫在playbook一個(gè)文件中的,而如果要變成角色,要扮演成一個(gè)單獨(dú)的目錄,角色名就是目錄名;
每一個(gè)角色一般按固定格式定義,任何角色都不能引用自己目錄以外的資源,這樣把這個(gè)目錄復(fù)制到任何主機(jī)上都可以用,這就是自包含應(yīng)該指明file子目錄;所有的模板放在templates子目錄下;所有的任務(wù)放在tasks子目錄下,所有的處理器放在handlers子目錄下;所有變量放在vars子目錄下;還有一個(gè)補(bǔ)充meta子目錄;
不是所有目錄必須得有,一般用到哪些目錄,就給出哪些目錄即可;這就是角色的目錄組織形式;
角色(role);/etc/ansible/roles也可在ansible.cfg中定義;
一般為目錄,每一個(gè)角色就是一個(gè)子目錄;

  • 角色集合:
    roles/
    mysql/
    httpd/
    nginx/
    memcached/

  • 每個(gè)角色,以特定的層級目錄結(jié)構(gòu)進(jìn)行組織:
    mysql/
    files/:存放由copy或script模塊等調(diào)用的文件;
    templates/:存放為template模塊查找所需的模板文件目錄;
    tasks/:至少應(yīng)該包含一個(gè)名為main.yml的文件;其它文件需要在此文件中通過include進(jìn)行包含;
    handlers/:至少應(yīng)該包含一個(gè)名為main.yml的文件;其它文件需要在此文件中通過include進(jìn)行包含;
    vars/:至少應(yīng)該包含一個(gè)名為main.yml的文件;其它文件需要在此文件中通過include進(jìn)行包含;
    meta/:定義當(dāng)前角色的特殊設(shè)定及其依賴關(guān)系;至少應(yīng)該包含一個(gè)名為main.yml的文件;其它文件需要在此文件中通過include進(jìn)行包含;
    default/:設(shè)定默認(rèn)變量時(shí)使用此目錄中的main.yml文件;

  • 在playbook調(diào)用角色方法1:

      - hosts: websrvs
        remote_user: root
        roles:
        - mysql
        - memcached
        - nginx

  • 在playbook調(diào)用角色方法2:在角色調(diào)用時(shí),傳遞變量給角色
      - hosts: 
        remote_user:
        roles:
        - { role: nginx, username: nginx }
       #鍵role用于指定角色名稱,后續(xù)的k/v用于傳遞變量給角色;

     #還可以基于條件測試實(shí)現(xiàn)角色調(diào)用;
      roles:
      - { role: nginx, when: "ansible_distribution_major_version == '7'" }

Ansible實(shí)現(xiàn)主/備模式主可用

ansible主備高可用.png
  1. 安裝ansible
    [root@localhost ~]# yum -y install ansible keepalive

  2. 編輯主機(jī)清單

  [root@localhost ~]# vim /etc/ansible/host
    [websrvs]
    192.168.1.115
    192.168.1.116 
    [hasrvs]
    192.168.1.10
    192.168.1.11
  1. 創(chuàng)建固定目錄結(jié)構(gòu)
[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

  1. 基于密鑰連接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

  1. 編輯roles
   [root@localhost ~]#  vim /etc/ansible/roles/keepalived/tasks/main.yml
#編輯如下內(nèi)容     
        - 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

  1. 編輯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

  1. 配置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

  1. 編輯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

  1. 測試并執(zhí)行
[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

  1. 訪問測試
 [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:規(guī)則已生成
 [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>

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容