自動化運維(二)——Ansible

23.3 Ansible

Ansible和Saltstack比較類似,都是基於Python開發的,Ansible不需要安裝客戶端,通過ssh去通信。

Ansible有以下優點:

1. 基於模塊工作,模塊可以由任何語言開發;
2. 支持命令行使用模塊,支持編寫yaml格式的playbook,易於編寫和閱讀;
3. 安裝簡單,CentOS上可直接yum安裝;
4. 有提供UI(瀏覽器圖形化),只是要收費,www.ansible.com/tower ;

官方文檔:https://docs.ansible.com/ansible/latest/index.html ,目前ansible已經被redhat公司收購,它在gitlab上是一個非常受歡迎的開源軟件,gitlab地址:https://gitlab.com/ansible/ansible

這裏推薦一本不錯的入門電子書:https://ansible-book.gitbooks.io/ansible-first-book/

安裝Ansible

準備兩臺機器,前面做saltstack有使用兩臺機器lzx和lzx1,IP分別是:192.168.100.150和192.168.100.160。

  • 只需要在lzx上安裝ansible:
[root@lzx ~]# yum list |grep ansible
ansible.noarch                           2.6.3-1.el7                   epel     
ansible-doc.noarch                       2.6.3-1.el7                   epel     
ansible-inventory-grapher.noarch         2.4.4-1.el7                   epel     
ansible-lint.noarch                      3.4.21-1.el7                  epel     
ansible-openstack-modules.noarch         0-20140902git79d751a.el7      epel     
ansible-review.noarch                    0.13.4-1.el7                  epel     
kubernetes-ansible.noarch                0.6.0-0.1.gitd65ebd5.el7      epel     
python2-ansible-runner.noarch            1.0.1-1.el7                   epel     
python2-ansible-tower-cli.noarch         3.3.0-2.el7                   epel     
[root@lzx ~]# yum install -y ansible
  • 密鑰認證:
    lzx上執行
[root@lzx ~]# ls ~/.ssh/
id_rsa  id_rsa.pub  known_hosts         //有id_rsa和id_rsa.pub,沒有的話執行ssh-keygen -t rsa,-t 指定密鑰類型
[root@lzx ~]# cat ~/.ssh/id_rsa.pub 
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDqWlFPl3JwzR3AiJgolBthMJradp2r1UekJZnnU5hVjDb+pZ72YQUfNdatuUMr96avQYsF+V61sOc/cxa3YPn35n36TW8P+u7FMxZf31eqMatcHG/AWvjW0UsDw+zQrBr5414mj+AIYQgj0GtDIQJbfifGizK7i9UPLy7oW3Ss7+G2+fqhJ2hIo6qTSBHwSdN3rn9ypL0dPIEqJyaaBUpg5a5JKv3KHO5EyJt6Z787SPf3snKddQNpLkgoQ8yPcbZQ3BE5gt6DapMMpLEUUR2adIfe0rWqcDr4Gp9QTW0u+/LgFI6I1UKdTVYvU2UkpUf4WEp+6Q8AROasXxljrNC1 root@lzx
[root@lzx ~]# vim .ssh/authorized_keys           //寫入下面內容,相當於給127.0.0.1做認證
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDqWlFPl3JwzR3AiJgolBthMJradp2r1UekJZnnU5hVjDb+pZ72YQUfNdatuUMr96avQYsF+V61sOc/cxa3YPn35n36TW8P+u7FMxZf31eqMatcHG/AWvjW0UsDw+zQrBr5414mj+AIYQgj0GtDIQJbfifGizK7i9UPLy7oW3Ss7+G2+fqhJ2hIo6qTSBHwSdN3rn9ypL0dPIEqJyaaBUpg5a5JKv3KHO5EyJt6Z787SPf3snKddQNpLkgoQ8yPcbZQ3BE5gt6DapMMpLEUUR2adIfe0rWqcDr4Gp9QTW0u+/LgFI6I1UKdTVYvU2UkpUf4WEp+6Q8AROasXxljrNC1 root@lzx

lzx1上執行

[root@lzx1 ~]# mkdir .ssh 
[root@lzx1 ~]# vim .ssh/authorized_keys          //寫入下面內容
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDqWlFPl3JwzR3AiJgolBthMJradp2r1UekJZnnU5hVjDb+pZ72YQUfNdatuUMr96avQYsF+V61sOc/cxa3YPn35n36TW8P+u7FMxZf31eqMatcHG/AWvjW0UsDw+zQrBr5414mj+AIYQgj0GtDIQJbfifGizK7i9UPLy7oW3Ss7+G2+fqhJ2hIo6qTSBHwSdN3rn9ypL0dPIEqJyaaBUpg5a5JKv3KHO5EyJt6Z787SPf3snKddQNpLkgoQ8yPcbZQ3BE5gt6DapMMpLEUUR2adIfe0rWqcDr4Gp9QTW0u+/LgFI6I1UKdTVYvU2UkpUf4WEp+6Q8AROasXxljrNC1 root@lzx

lzx上執行

[root@lzx ~]# ssh lzx1         //要先配置/etc/hosts文件才能識別
The authenticity of host 'lzx1 (192.168.100.160)' can't be established.
ECDSA key fingerprint is SHA256:teKu3atU+OByPeXXD2xXhyb30vg6nW8ETqqCr785Dbc.
ECDSA key fingerprint is MD5:13:a4:f1:c0:1f:62:65:d4:f4:4e:42:ab:40:f1:36:60.
Are you sure you want to continue connecting (yes/no)? yes        //輸入yes
Warning: Permanently added 'lzx1' (ECDSA) to the list of known hosts.
Enter passphrase for key '/root/.ssh/id_rsa':         //沒設置密鑰的密碼就直接回車,有就輸入
root@lzx1's password:          //輸入lzx1機器上的root密碼
Last login: Tue Sep 11 10:12:52 2018 from 192.168.100.1
[root@lzx1 ~]# logout
Connection to lzx1 closed.
  • lzx上修改配置文件:
[root@lzx ~]# vim /etc/ansible/hosts        //添加下面內容
[testhost]         //自定義主機組名字
127.0.0.1
lzx1          //這兩行可以是ip或主機名

遠程執行命令

  • lzx上執行命令:
[root@lzx ~]# ansible testhost -m command -a 'w'         //-m,指定模塊;-a,指定命令
Enter passphrase for key '/root/.ssh/id_rsa':            //輸入生成密鑰時設置的密碼
127.0.0.1 | SUCCESS | rc=0 >>          
 10:28:34 up  1:47,  2 users,  load average: 0.17, 0.07, 0.06
USER     TTY      FROM             LOGIN@   IDLE   JCPU   PCPU WHAT
root     pts/0    192.168.100.1    08:41    2.00s  0.81s  0.00s ssh -C -o ControlMaster=auto -o ControlPersist=60s -o KbdInteractiveAuthentication=no -o PreferredAuthentications=gssapi-with-mic,gssapi-keyex,hostbased,publickey -o PasswordAuthentication=no -o ConnectTimeout=10 -o ControlPath=/root/.ansible/cp/21f0e6a9ae -tt 127.0.0.1 /bin/sh -c '/usr/bin/python /root/.ansible/tmp/ansible-tmp-1536676113.66-66256136181906/command.py && sleep 0'
root     pts/3    127.0.0.1        10:28    1.00s  0.12s  0.04s w

Enter passphrase for key '/root/.ssh/id_rsa':            //輸入生成密鑰時設置的密碼  
lzx1 | SUCCESS | rc=0 >>
 10:28:42 up 16 min,  2 users,  load average: 0.04, 0.03, 0.05
USER     TTY      FROM             LOGIN@   IDLE   JCPU   PCPU WHAT
root     pts/0    192.168.100.1    10:12   13:06   0.01s  0.01s -bash
root     pts/1    192.168.100.150  10:28    1.00s  0.06s  0.00s w

正常輸出爲綠色顯示,出錯爲紅色顯示

[root@lzx ~]# ansible testhost -m command -a 'hostname'
Enter passphrase for key '/root/.ssh/id_rsa': Enter passphrase for key '/root/.ssh/id_rsa':         //輸入生成密鑰時設置的密碼
127.0.0.1 | SUCCESS | rc=0 >>
lzx
              //輸入生成密鑰時設置的密碼
lzx1 | SUCCESS | rc=0 >>
lzx1

也可以指定單獨一臺機器,不指定主機組

[root@lzx ~]# ansible lzx1 -m command -a 'hostname'
Enter passphrase for key '/root/.ssh/id_rsa':        //輸入生成密鑰時設置的密碼
lzx1 | SUCCESS | rc=0 >>
lzx1

如果執行命令時遇到報錯:“msg”:“Aborting,target uses selinux but python bindings (libselinux-python) aren’t installed”,直接yum安裝 libselinux-python

另外,還可以只有shell模塊來執行命令,多用於遠程執行腳本

[root@lzx ~]# ansible lzx1 -m shell -a 'date'
Enter passphrase for key '/root/.ssh/id_rsa':       //輸入生成密鑰時設置的密碼
lzx1 | SUCCESS | rc=0 >>
Tue Sep 11 10:39:03 EDT 2018

拷貝文件或目錄

拷貝目錄時,如果目標指定的目錄不存在,它會自動創建;如果存在,源目錄會放到目標目錄下面。拷貝文件時,dest指定的名字和源如果不同,並且它不是已經存在的目錄,相當於拷貝過去後又重命名;如果目錄存在,則會把文件放在目標目錄下面。

  • 拷貝目錄:
[root@lzx ~]# ansible lzx1 -m copy -a "src=/etc/ansible dest=/tmp/ansible_test owner=root group=root mode=0755"      //copy表示copy模塊;src表示源目錄;dest表示目標目錄;owner指定屬主;group指定屬組;mode指定權限
Enter passphrase for key '/root/.ssh/id_rsa':       //輸入生成密鑰時設置的密碼
lzx1 | SUCCESS => {
    "changed": true, 
    "dest": "/tmp/ansible_test/", 
    "src": "/etc/ansible"
}

到lzx1上查看

[root@lzx1 ~]# ls /tmp/
ansible_test          //有剛剛拷貝的目錄
mongodb-27019.sock
systemd-private-645901bd56d24e14989826d0df1dc26e-chronyd.service-6UuejO
systemd-private-645901bd56d24e14989826d0df1dc26e-systemd-hostnamed.service-H4iKJy
systemd-private-a6ad68ff8ed74d66ad66a5232a07dab2-chronyd.service-ElmwW4
systemd-private-c40d86d5546d46c68cbb031445b13d64-chronyd.service-LsRxBQ
systemd-private-f43349b80b2a416d9ea1b177669c618f-chronyd.service-sBY7OV
[root@lzx1 ~]# ls -lt /tmp/ansible_test/
total 0
drwxr-xr-x 3 root root 51 Sep 13 08:51 ansible         //屬主屬組權限都能對應
  • 拷貝文件:
[root@lzx ~]# ansible lzx1 -m copy -a "src=/etc/passwd dest=/tmp owner=root group=root mode=0755"
Enter passphrase for key '/root/.ssh/id_rsa':       //輸入生成密鑰時設置的密碼
lzx1 | SUCCESS => {
    "changed": true, 
    "checksum": "eb0190ef77febf56f7950c9d54c5799cdfa32ee4", 
    "dest": "/tmp/passwd", 
    "gid": 0,
    "group": "root", 
    "md5sum": "55996731e2df563a71a4b9d66094c96c", 
    "mode": "0755", 
    "owner": "root", 
    "size": 1042, 
    "src": "/root/.ansible/tmp/ansible-tmp-1536843628.27-199330051688323/source", 
    "state": "file", 
    "uid": 0
}

到lzx1上查看

[root@lzx1 ~]# ls /tmp/
ansible_test
mongodb-27019.sock
passwd         //有剛剛拷貝的文件
systemd-private-645901bd56d24e14989826d0df1dc26e-chronyd.service-6UuejO
systemd-private-645901bd56d24e14989826d0df1dc26e-systemd-hostnamed.service-H4iKJy
systemd-private-a6ad68ff8ed74d66ad66a5232a07dab2-chronyd.service-ElmwW4
systemd-private-c40d86d5546d46c68cbb031445b13d64-chronyd.service-LsRxBQ
systemd-private-f43349b80b2a416d9ea1b177669c618f-chronyd.service-sBY7OV
[root@lzx1 ~]# ls -lt /tmp/passwd 
-rwxr-xr-x 1 root root 1042 Sep 13 09:00 /tmp/passwd

遠程執行腳本

  • 編輯腳本:
[root@lzx ~]# vim /tmp/1.sh         //寫入下面內容
#!/bin/bash
echo `date` > /tmp/123.txt
  • 分發腳本:
[root@lzx ~]# ansible testhost -m copy -a "src=/tmp/1.sh dest=/tmp/test.sh mode=0755"        //拷貝腳本到個機器上
Enter passphrase for key '/root/.ssh/id_rsa':            //輸入生成密鑰時設置的密碼 
19lzx1 | SUCCESS => {
    "changed": true, 
    "checksum": "605c9fa9907b29503e55e10a40e5edf313dda056", 
    "dest": "/tmp/test.sh", 
    "gid": 0, 
    "group": "root", 
    "md5sum": "8877086180b43853c86af1d55989a0b6", 
    "mode": "0755", 
    "owner": "root", 
    "size": 39, 
    "src": "/root/.ansible/tmp/ansible-tmp-1536844193.91-149423054827561/source", 
    "state": "file", 
    "uid": 0
}

Enter passphrase for key '/root/.ssh/id_rsa':            //輸入生成密鑰時設置的密碼
127.0.0.1 | SUCCESS => {
    "changed": true, 
    "checksum": "605c9fa9907b29503e55e10a40e5edf313dda056", 
    "dest": "/tmp/test.sh", 
    "gid": 0, 
    "group": "root", 
    "md5sum": "8877086180b43853c86af1d55989a0b6", 
    "mode": "0755", 
    "owner": "root", 
    "size": 39, 
    "src": "/root/.ansible/tmp/ansible-tmp-1536844193.91-3493264441468/source", 
    "state": "file", 
    "uid": 0
}

[root@lzx ~]# ls /tmp/ |grep test
test.sh         //拷貝成功

到lzx1上查看

[root@lzx1 ~]# ls /tmp/ |grep test
ansible_test
test.sh        //拷貝成功
  • 執行腳本:
[root@lzx ~]# ansible testhost -m shell -a "/tmp/test.sh"         //shell表示shell模塊
Enter passphrase for key '/root/.ssh/id_rsa':            //輸入生成密鑰時設置的密碼
lzx1 | SUCCESS | rc=0 >>

Enter passphrase for key '/root/.ssh/id_rsa':            //輸入生成密鑰時設置的密碼
127.0.0.1 | SUCCESS | rc=0 >>
  • 查看結果:
[root@lzx ~]# cat /tmp/123.txt 
Thu Sep 13 09:15:17 EDT 2018

lzx1上查看

[root@lzx1 ~]# cat /tmp/123.txt 
Thu Sep 13 09:15:12 EDT 2018

shell模塊除了支持執行腳本之外,還可以帶管道,而command模塊不支持帶管道。

  • 使用command模塊測試:
[root@lzx ~]# ansible testhost -m command -a "cat /etc/passwd|wc -l"
Enter passphrase for key '/root/.ssh/id_rsa':            //輸入生成密鑰時設置的密碼
lzx1 | FAILED | rc=1 >>
cat: invalid option -- 'l'
Try 'cat --help' for more information.non-zero return code

Enter passphrase for key '/root/.ssh/id_rsa':            //輸入生成密鑰時設置的密碼
127.0.0.1 | FAILED | rc=1 >>
cat: invalid option -- 'l'
Try 'cat --help' for more information.non-zero return code
  • 使用shell模塊測試:
[root@lzx ~]# ansible testhost -m shell -a "cat /etc/passwd|wc -l"
Enter passphrase for key '/root/.ssh/id_rsa':            //輸入生成密鑰時設置的密碼
127.0.0.1 | SUCCESS | rc=0 >>
22

Enter passphrase for key '/root/.ssh/id_rsa':            //輸入生成密鑰時設置的密碼
lzx1 | SUCCESS | rc=0 >>
23

管理任務計劃

ansible用來管理任務計劃的模塊是cron。

  • 創建任務計劃:
[root@lzx ~]# ansible lzx1 -m cron -a "name='test cron' job='/bin/touch /tmp/aaa.txt' weekday=6"       //創建任務計劃,name指定任務計劃名;job指定任務計劃具體操作;weedday指定任務計劃執行日期,有對應分時日月周
Enter passphrase for key '/root/.ssh/id_rsa':            //輸入生成密鑰時設置的密碼
lzx1 | SUCCESS => {
    "changed": true, 
    "envs": [], 
    "jobs": [
        "test corn"
    ]
}

帶lzx1上查看

[root@lzx1 ~]# crontab -l
#Ansible: test cron            //這一行不能改動,否則管理時會出錯
* * * * 6 /bin/touch /tmp/aaa.txt          //創建成功,與上面一一對應
  • 刪除任務計劃:
[root@lzx ~]# ansible lzx1 -m cron -a "name='test cron' state=absent"       //刪除任務計劃
Enter passphrase for key '/root/.ssh/id_rsa':            //輸入生成密鑰時設置的密碼
lzx1 | SUCCESS => {
    "changed": true,        //需要注意這個有可能是false,這時就表明沒有發生變化
    "envs": [], 
    "jobs": []
}

到lzx1上查看

[root@lzx1 ~]# crontab -l
[root@lzx1 ~]#           //沒有任務計劃

其他的時間表示:
分鐘 minute 小時 hour 日期 day 月份 month

安裝包和管理服務

ansible安裝包使用的是yum模塊,管理服務的是service模塊。

  • 安裝包:
[root@lzx ~]# ansible testhost -m yum -a "name=httpd"          //給lzx1安裝httpd
Enter passphrase for key '/root/.ssh/id_rsa':            //輸入生成密鑰時設置的密碼
lzx1 | SUCCESS => {
    "changed": true, 
    "msg": "", 
    "rc": 0, 
    "results": [
        "Loaded plugins: fastestmirror\nLoading mirror speeds from cached hostfile\n * base: mirrors.cn99.com\n * epel: mirror01.idc.hinet.net\n * extras: mirrors.cn99.com\n * updates: mirrors.shu.edu.cn\nResolving Dependencies\n--> Running transaction check\n---> Package httpd.x86_64 0:2.4.6-80.el7.centos.1 will be installed\n--> Processing Dependency: httpd-tools = 2.4.6-80.el7.centos.1 for package: httpd-2.4.6-80.el7.centos.1.x86_64\n--> Processing Dependency: /etc/mime.types for package: httpd-2.4.6-80.el7.centos.1.x86_64\n--> Running transaction check\n---> Package httpd-tools.x86_64 0:2.4.6-80.el7.centos.1 will be installed\n---> Package mailcap.noarch 0:2.1.41-2.el7 will be installed\n--> Finished Dependency Resolution\n\nDependencies Resolved\n\n================================================================================\n Package           Arch         Version                     Repository     Size\n================================================================================\nInstalling:\n httpd             x86_64       2.4.6-80.el7.centos.1       updates       2.7 M\nInstalling for dependencies:\n httpd-tools       x86_64       2.4.6-80.el7.centos.1       updates        90 k\n mailcap           noarch       2.1.41-2.el7                base           31 k\n\nTransaction Summary\n================================================================================\nInstall  1 Package (+2 Dependent packages)\n\nTotal download size: 2.8 M\nInstalled size: 9.6 M\nDownloading packages:\n--------------------------------------------------------------------------------\nTotal                                              1.1 MB/s | 2.8 MB  00:02     \nRunning transaction check\nRunning transaction test\nTransaction test succeeded\nRunning transaction\n  Installing : httpd-tools-2.4.6-80.el7.centos.1.x86_64                     1/3 \n  Installing : mailcap-2.1.41-2.el7.noarch                                  2/3 \n  Installing : httpd-2.4.6-80.el7.centos.1.x86_64                           3/3 \n  Verifying  : mailcap-2.1.41-2.el7.noarch                                  1/3 \n  Verifying  : httpd-tools-2.4.6-80.el7.centos.1.x86_64                     2/3 \n  Verifying  : httpd-2.4.6-80.el7.centos.1.x86_64                           3/3 \n\nInstalled:\n  httpd.x86_64 0:2.4.6-80.el7.centos.1                                          \n\nDependency Installed:\n  httpd-tools.x86_64 0:2.4.6-80.el7.centos.1    mailcap.noarch 0:2.1.41-2.el7   \n\nComplete!\n"
    ]
}

到lzx1上查看:

[root@lzx1 ~]# rpm -qa |grep httpd
httpd-tools-2.4.6-80.el7.centos.1.x86_64
httpd-2.4.6-80.el7.centos.1.x86_64           //版本與上面安裝的一致
  • 卸載包:
[root@lzx ~]# ansible lzx1 -m yum -a "name=httpd state=removed"          //卸載lzx1上面的httpd
Enter passphrase for key '/root/.ssh/id_rsa':            //輸入生成密鑰時設置的密碼
lzx1 | SUCCESS => {
    "changed": true, 
    "msg": "", 
    "rc": 0, 
    "results": [
        "Loaded plugins: fastestmirror\nResolving Dependencies\n--> Running transaction check\n---> Package httpd.x86_64 0:2.4.6-80.el7.centos.1 will be erased\n--> Finished Dependency Resolution\n\nDependencies Resolved\n\n================================================================================\n Package      Arch          Version                       Repository       Size\n================================================================================\nRemoving:\n httpd        x86_64        2.4.6-80.el7.centos.1         @updates        9.4 M\n\nTransaction Summary\n================================================================================\nRemove  1 Package\n\nInstalled size: 9.4 M\nDownloading packages:\nRunning transaction check\nRunning transaction test\nTransaction test succeeded\nRunning transaction\n  Erasing    : httpd-2.4.6-80.el7.centos.1.x86_64                           1/1 \n  Verifying  : httpd-2.4.6-80.el7.centos.1.x86_64                           1/1 \n\nRemoved:\n  httpd.x86_64 0:2.4.6-80.el7.centos.1                                          \n\nComplete!\n"
    ]
}

到lzx1上查看:

[root@lzx1 ~]# rpm -qa |grep httpd
httpd-tools-2.4.6-80.el7.centos.1.x86_64          //httpd已經被卸載
  • 啓動服務:
[root@lzx ~]# ansible lzx1 -m yum -a "name=httpd"      //爲了測試下面啓動服務再次給lzxx1裝上httpd
[root@lzx ~]# ansible lzx1 -m service -a "name=httpd state=started enabled=yes"       //給lzx1啓動httpd服務,並且設置爲開機啓動
Enter passphrase for key '/root/.ssh/id_rsa':            //輸入生成密鑰時設置的密碼
lzx1 | SUCCESS => {
    "changed": true, 
    "enabled": true, 
    "name": "httpd", 
    "state": "started", 
    "status": {
        "ActiveEnterTimestampMonotonic": "0", 
        "ActiveExitTimestampMonotonic": "0", 
        "ActiveState": "inactive", 
        "After": "-.mount basic.target system.slice systemd-journald.socket network.target remote-fs.target nss-lookup.target tmp.mount", 
        "AllowIsolate": "no", 
        "AmbientCapabilities": "0", 
        "AssertResult": "no", 
        "AssertTimestampMonotonic": "0", 
        "Before": "shutdown.target", 
        "BlockIOAccounting": "no", 
        "BlockIOWeight": "18446744073709551615", 
        "CPUAccounting": "no", 
        "CPUQuotaPerSecUSec": "infinity", 
        "CPUSchedulingPolicy": "0", 
        "CPUSchedulingPriority": "0", 
        "CPUSchedulingResetOnFork": "no", 
        "CPUShares": "18446744073709551615", 
        "CanIsolate": "no", 
        "CanReload": "yes", 
        "CanStart": "yes", 
        "CanStop": "yes", 
        "CapabilityBoundingSet": "18446744073709551615", 
        "ConditionResult": "no", 
        "ConditionTimestampMonotonic": "0", 
        "Conflicts": "shutdown.target", 
        "ControlPID": "0", 
        "DefaultDependencies": "yes", 
        "Delegate": "no", 
        "Description": "The Apache HTTP Server", 
        "DevicePolicy": "auto", 
        "Documentation": "man:httpd(8) man:apachectl(8)", 
        "EnvironmentFile": "/etc/sysconfig/httpd (ignore_errors=no)", 
        "ExecMainCode": "0", 
        "ExecMainExitTimestampMonotonic": "0", 
        "ExecMainPID": "0", 
        "ExecMainStartTimestampMonotonic": "0", 
        "ExecMainStatus": "0", 
        "ExecReload": "{ path=/usr/sbin/httpd ; argv[]=/usr/sbin/httpd $OPTIONS -k graceful ; ignore_errors=no ; start_time=[n/a] ; stop_time=[n/a] ; pid=0 ; code=(null) ; status=0/0 }", 
        "ExecStart": "{ path=/usr/sbin/httpd ; argv[]=/usr/sbin/httpd $OPTIONS -DFOREGROUND ; ignore_errors=no ; start_time=[n/a] ; stop_time=[n/a] ; pid=0 ; code=(null) ; status=0/0 }", 
        "ExecStop": "{ path=/bin/kill ; argv[]=/bin/kill -WINCH ${MAINPID} ; ignore_errors=no ; start_time=[n/a] ; stop_time=[n/a] ; pid=0 ; code=(null) ; status=0/0 }", 
        "FailureAction": "none", 
        "FileDescriptorStoreMax": "0", 
        "FragmentPath": "/usr/lib/systemd/system/httpd.service", 
        "GuessMainPID": "yes", 
        "IOScheduling": "0", 
        "Id": "httpd.service", 
        "IgnoreOnIsolate": "no", 
        "IgnoreOnSnapshot": "no", 
        "IgnoreSIGPIPE": "yes", 
        "InactiveEnterTimestampMonotonic": "0", 
        "InactiveExitTimestampMonotonic": "0", 
        "JobTimeoutAction": "none", 
        "JobTimeoutUSec": "0", 
        "KillMode": "control-group", 
        "KillSignal": "18", 
        "LimitAS": "18446744073709551615", 
        "LimitCORE": "18446744073709551615", 
        "LimitCPU": "18446744073709551615", 
        "LimitDATA": "18446744073709551615", 
        "LimitFSIZE": "18446744073709551615", 
        "LimitLOCKS": "18446744073709551615", 
        "LimitMEMLOCK": "65536", 
        "LimitMSGQUEUE": "819200", 
        "LimitNICE": "0", 
        "LimitNOFILE": "4096", 
        "LimitNPROC": "3828", 
        "LimitRSS": "18446744073709551615", 
        "LimitRTPRIO": "0", 
        "LimitRTTIME": "18446744073709551615", 
        "LimitSIGPENDING": "3828", 
        "LimitSTACK": "18446744073709551615", 
        "LoadState": "loaded", 
        "MainPID": "0", 
        "MemoryAccounting": "no", 
        "MemoryCurrent": "18446744073709551615", 
        "MemoryLimit": "18446744073709551615", 
        "MountFlags": "0", 
        "Names": "httpd.service", 
        "NeedDaemonReload": "no", 
        "Nice": "0", 
        "NoNewPrivileges": "no", 
        "NonBlocking": "no", 
        "NotifyAccess": "main", 
        "OOMScoreAdjust": "0", 
        "OnFailureJobMode": "replace", 
        "PermissionsStartOnly": "no", 
        "PrivateDevices": "no", 
        "PrivateNetwork": "no", 
        "PrivateTmp": "yes", 
        "ProtectHome": "no", 
        "ProtectSystem": "no", 
        "RefuseManualStart": "no", 
        "RefuseManualStop": "no", 
        "RemainAfterExit": "no", 
        "Requires": "basic.target -.mount", 
        "RequiresMountsFor": "/var/tmp", 
        "Restart": "no", 
        "RestartUSec": "100ms", 
        "Result": "success", 
        "RootDirectoryStartOnly": "no", 
        "RuntimeDirectoryMode": "0755", 
        "SameProcessGroup": "no", 
        "SecureBits": "0", 
        "SendSIGHUP": "no", 
        "SendSIGKILL": "yes", 
        "Slice": "system.slice", 
        "StandardError": "inherit", 
        "StandardInput": "null", 
        "StandardOutput": "journal", 
        "StartLimitAction": "none", 
        "StartLimitBurst": "5", 
        "StartLimitInterval": "10000000", 
        "StartupBlockIOWeight": "18446744073709551615", 
        "StartupCPUShares": "18446744073709551615", 
        "StatusErrno": "0", 
        "StopWhenUnneeded": "no", 
        "SubState": "dead", 
        "SyslogLevelPrefix": "yes", 
        "SyslogPriority": "30", 
        "SystemCallErrorNumber": "0", 
        "TTYReset": "no", 
        "TTYVHangup": "no", 
        "TTYVTDisallocate": "no", 
        "TasksAccounting": "no", 
        "TasksCurrent": "18446744073709551615", 
        "TasksMax": "18446744073709551615", 
        "TimeoutStartUSec": "1min 30s", 
        "TimeoutStopUSec": "1min 30s", 
        "TimerSlackNSec": "50000", 
        "Transient": "no", 
        "Type": "notify", 
        "UMask": "0022", 
        "UnitFilePreset": "disabled", 
        "UnitFileState": "disabled", 
        "Wants": "system.slice", 
        "WatchdogTimestampMonotonic": "0", 
        "WatchdogUSec": "0"
    }
}

到lzx1上查看:

[root@lzx1 ~]# ps aux |grep httpd
root       1345  0.0  0.4 221972  4972 ?        Ss   09:05   0:00 /usr/sbin/httpd -DFOREGROUND
apache     1346  0.0  0.2 221972  2964 ?        S    09:05   0:00 /usr/sbin/httpd -DFOREGROUND
apache     1347  0.0  0.2 221972  2964 ?        S    09:05   0:00 /usr/sbin/httpd -DFOREGROUND
apache     1348  0.0  0.2 221972  2964 ?        S    09:05   0:00 /usr/sbin/httpd -DFOREGROUND
apache     1349  0.0  0.2 221972  2964 ?        S    09:05   0:00 /usr/sbin/httpd -DFOREGROUND
apache     1351  0.0  0.2 221972  2964 ?        S    09:05   0:00 /usr/sbin/httpd -DFOREGROUND
root       1362  0.0  0.0 112704   972 pts/0    R+   09:07   0:00 grep --color=auto httpd       //httpd服務已經啓動

[root@lzx1 ~]# systemctl list-unit-files |grep httpd         //centos7上查看開機啓動項
httpd.service                                 enabled         //httpd服務已經開機啓動
  • 列出ansible的所有模塊:
[root@lzx ~]# ansible-doc -l         //列出ansible的所有模塊,太多,不一一列舉
  • 查看某個模塊的文檔:
[root@lzx ~]# ansible-doc service         //查看service模塊的文檔
> SERVICE    (/usr/lib/python2.7/site-packages/ansible/modules/system/service.py)

        Controls services on remote hosts. Supported init systems include BSD init,
        OpenRC, SysV, Solaris SMF, systemd, upstart. For Windows targets, use the
        [win_service] module instead.

  * note: This module has a corresponding action plugin.

OPTIONS (= is mandatory):

- arguments
        Additional arguments provided on the command line
        (Aliases: args)[Default: (null)]

- enabled
        Whether the service should start on boot. *At least one of state and enabled
        are required.*
        [Default: (null)]
        type: bool

= name
        Name of the service.


- pattern
        If the service does not respond to the status command, name a substring to
        look for as would be found in the output of the `ps' command as a stand-in for
        a status result.  If the string is found, the service will be assumed to be
        running.
        [Default: (null)]
        version_added: 0.7

- runlevel
        For OpenRC init scripts (ex: Gentoo) only.  The runlevel that this service
        belongs to.
        [Default: default]

- sleep
        If the service is being `restarted' then sleep this many seconds between the
        stop and start command. This helps to workaround badly behaving init scripts
        that exit immediately after signaling a process to stop.
        [Default: (null)]
        version_added: 1.3

- state
        `started'/`stopped' are idempotent actions that will not run commands unless
        necessary.  `restarted' will always bounce the service.  `reloaded' will
        always reload. *At least one of state and enabled are required.* Note that
        reloaded will start the service if it is not already started, even if your
        chosen init system wouldn't normally.
        (Choices: reloaded, restarted, running, started, stopped)[Default: (null)]

- use
        The service module actually uses system specific modules, normally through
        auto detection, this setting can force a specific module.
        Normally it uses the value of the 'ansible_service_mgr' fact and falls back to
        the old 'service' module when none matching is found.
        [Default: auto]
        version_added: 2.2
        


NOTES:
      * For Windows targets, use the [win_service] module instead.

AUTHOR: Ansible Core Team, Michael DeHaan
        METADATA:
          status:
          - stableinterface
          supported_by: core
        

EXAMPLES:
- name: Start service httpd, if not running
  service:
    name: httpd
    state: started

- name: Stop service httpd, if running
  service:
    name: httpd
    state: stopped

- name: Restart service httpd, in all cases
  service:
    name: httpd
    state: restarted

- name: Reload service httpd, in all cases
  service:
    name: httpd
    state: reloaded

- name: Enable service httpd, and not touch the running state
  service:
    name: httpd
    enabled: yes

- name: Start service foo, based on running process /usr/bin/foo
  service:
    name: foo
    pattern: /usr/bin/foo
    state: started

- name: Restart network service for interface eth0
  service:
    name: network
    state: restarted
    args: eth0

使用ansible playbook

playbook相當於把模塊寫入到配置文件裏面,這樣就避免我們在命令行下頻繁地敲命令。

  • 編輯一個簡單的playbook:
[root@lzx ~]# cd /etc/ansible/
[root@lzx ansible]# vim test.yml       //寫入下面內容,playbook以.yml作爲後綴名,注意空格
---       //表示開頭,不可忽略
- hosts: lzx1        //指定針對哪些主機操作,多個主機用逗號分隔,也可以使用主機組
  remote_user: root       //指定執行用戶
  tasks:         //指定任務
    - name: test_playbook       //描述任務,後面執行過程中會顯示出來     
      shell: touch /tmp/lzxlzx.txt          //具體任務內容,shell表示shell模塊
  • 執行上面的playbook:
[root@lzx ansible]# ansible-playbook test.yml       //執行playbook

PLAY [lzx1] ************************************************************************************************

TASK [Gathering Facts] *************************************************************************************
Enter passphrase for key '/root/.ssh/id_rsa':            //輸入生成密鑰時設置的密碼
ok: [lzx1]

TASK [test_playbook] ***************************************************************************************
 [WARNING]: Consider using the file module with state=touch rather than running touch.  If you need to use         //這裏提示使用playbook不如使用file module的state=touch選項
command because file is insufficient you can add warn=False to this command task or set
command_warnings=False in ansible.cfg to get rid of this message.

changed: [lzx1]

PLAY RECAP *************************************************************************************************
lzx1                       : ok=2    changed=1    unreachable=0    failed=0       //執行成功

到lzx1上查看:

[root@lzx1 ~]# ls -lt /tmp/lzxlzx.txt 
-rw-r--r-- 1 root root 0 Sep 18 09:33 /tmp/lzxlzx.txt          //文件已生成

playbook中的變量

  • 編輯一個創建用戶的playbook:
[root@lzx ansible]# vim create_user.yml        //寫入下面內容,注意空格
---
- name: create_user       //作用描述,後面執行過程中會顯示出來
  hosts: lzx1       //指定執行主機
  user: root        //指定執行用戶
  gather_facts: false        //gather_facts指定在下面任務執行前是否執行setup模塊獲取主機相關信息,false表示不獲取
  vars:        //指定變量
    - user: "test"        //定義了user變量
  tasks:         //指定任務
    - name: create user       //描述任務
      user: name= "{{ user }}"      //變量名要用引號括起來;user指定調用user模塊,name是user模塊裏面的參數,增加的用戶名字調用了上面user變量的值
  • 執行上面的playbook:
[root@lzx ansible]# ansible-playbook create_user.yml 

PLAY [create_user] *****************************************************************************************

TASK [create user] *****************************************************************************************
Enter passphrase for key '/root/.ssh/id_rsa':            //輸入生成密鑰時設置的密碼
ok: [lzx1]

changed: [lzx1]

PLAY RECAP *************************************************************************************************
lzx1                       : ok=1    changed=1    unreachable=0    failed=0        //表示執行成功

到lzx1上查看:

[root@lzx1 ~]# tail /etc/passwd
polkitd:x:999:997:User for polkitd:/:/sbin/nologin
postfix:x:89:89::/var/spool/postfix:/sbin/nologin
chrony:x:998:996::/var/lib/chrony:/sbin/nologin
sshd:x:74:74:Privilege-separated SSH:/var/empty/sshd:/sbin/nologin
admin:x:1000:1000:admin:/home/admin:/bin/bash
mongod:x:997:995:mongod:/var/lib/mongo:/bin/false
tss:x:59:59:Account used by the trousers package to sandbox the tcsd daemon:/dev/null:/sbin/nologin
git:x:1001:1001::/home/git:/usr/bin/git-shell
apache:x:48:48:Apache:/usr/share/httpd:/sbin/nologin
test:x:1002:1002::/home/test:/bin/bash         //這裏說明有test用戶

playbook中的循環

  • 編輯一個帶循環的playbook:
[root@lzx ansible]# vim while.yml         //寫入下面內容,注意空格
---
- hosts: lzx1
  user: root
  tasks:
    - name: change mode for files         //描述人物
      file: path=/tmp/{{ item }} state=touch mode=600         //使用file模塊,指定路徑和要操作的文件名和權限;state=touch 創建文件;items爲變量名
      with_items:        
        - 1.txt
        - 2.txt
        - 3.txt
  • 執行上面的playbook:
[root@lzx ansible]# ansible-playbook while.yml 

PLAY [lzx1] ************************************************************************************************

TASK [Gathering Facts] *************************************************************************************           //Gathering Facts表示在收集信息,不禁掉會自動收集信息
Enter passphrase for key '/root/.ssh/id_rsa':            //輸入生成密鑰時設置的密碼
ok: [lzx1]

TASK [change mode for files] *******************************************************************************
changed: [lzx1] => (item=1.txt)
changed: [lzx1] => (item=2.txt)
changed: [lzx1] => (item=3.txt)

PLAY RECAP *************************************************************************************************
lzx1                       : ok=2    changed=1    unreachable=0    failed=0   

到lzx1上查看

[root@lzx1 ~]# ls -l /tmp/
total 12
-rw-r--r-- 1 root   root     29 Sep 13 09:15 123.txt
-rw------- 1 root   root      0 Sep 20 08:18 1.txt
-rw------- 1 root   root      0 Sep 20 08:18 2.txt
-rw------- 1 root   root      0 Sep 20 08:18 3.txt          //這裏有剛剛創建的文件,並且權限均爲600
drwx------ 2 root   root     61 Sep 18 08:50 ansible_KZIszY
drwxr-xr-x 3 root   root     21 Sep 13 08:51 ansible_test
-rw-r--r-- 1 root   root      0 Sep 18 09:33 lzxlzx.txt
srwx------ 1 mongod mongod    0 Sep 20 07:23 mongodb-27019.sock
-rwxr-xr-x 1 root   root   1042 Sep 13 09:00 passwd
drwx------ 3 root   root     17 Sep 20 07:20 systemd-private-3f6f6b8540bb4821baa29cc8d067029b-chronyd.service-ATfvsa
drwx------ 3 root   root     17 Sep 20 07:21 systemd-private-3f6f6b8540bb4821baa29cc8d067029b-httpd.service-B5HtVK
drwx------ 3 root   root     17 Sep 20 07:20 systemd-private-3f6f6b8540bb4821baa29cc8d067029b-systemd-hostnamed.service-sfZTSG
drwx------ 3 root   root     17 Sep 20 07:23 systemd-private-48497924bb434b52bc8db3b05c607623-chronyd.service-cFxvoE
drwx------ 3 root   root     17 Sep 20 07:23 systemd-private-48497924bb434b52bc8db3b05c607623-httpd.service-PGzvnW
drwx------ 3 root   root     17 Sep 13 08:41 systemd-private-c40d86d5546d46c68cbb031445b13d64-chronyd.service-LsRxBQ

playbook中的條件判斷

  • 編輯一個帶條件判斷的playbook:
[root@lzx ansible]# vim when.yml          //寫入下面內容,注意空格
---
- hosts: testhost         //這裏如果單獨指定某一臺機器,那判斷條件就失效了
  user: root
  gather_facts: True         //這裏表示收集信息,不加這行默認也表示收集信息
  tasks:
    - name: use when     
      shell: touch /tmp/when.txt
      when: ansible_ens33.ipv4.address == "192.168.100.160"       //when模塊,代表條件判斷,這裏判斷的內容是在gather_facts收集的信息裏
  • 執行上面的playbook:
[root@lzx ansible]# ansible-playbook when.yml 

PLAY [testhost] ********************************************************************************************

TASK [Gathering Facts] *************************************************************************************
Enter passphrase for key '/root/.ssh/id_rsa':            //輸入生成密鑰時設置的密碼
ok: [lzx1]

Enter passphrase for key '/root/.ssh/id_rsa':            //輸入生成密鑰時設置的密碼
ok: [127.0.0.1]

TASK [use when] ********************************************************************************************
skipping: [127.0.0.1]
 [WARNING]: Consider using the file module with state=touch rather than running touch.  If you need to use
command because file is insufficient you can add warn=False to this command task or set
command_warnings=False in ansible.cfg to get rid of this message.

changed: [lzx1]

PLAY RECAP *************************************************************************************************
127.0.0.1                  : ok=1    changed=0    unreachable=0    failed=0   
lzx1                       : ok=2    changed=1    unreachable=0    failed=0      //lzx未發生變化,lzx1發生了變化

到lzx1上查看

[root@lzx1 ~]# ls -l /tmp/
total 12
-rw-r--r-- 1 root   root     29 Sep 13 09:15 123.txt
-rw------- 1 root   root      0 Sep 20 08:18 1.txt
-rw------- 1 root   root      0 Sep 20 08:18 2.txt
-rw------- 1 root   root      0 Sep 20 08:18 3.txt
drwx------ 2 root   root     61 Sep 18 08:50 ansible_KZIszY
drwxr-xr-x 3 root   root     21 Sep 13 08:51 ansible_test
-rw-r--r-- 1 root   root      0 Sep 18 09:33 lzxlzx.txt
srwx------ 1 mongod mongod    0 Sep 20 07:23 mongodb-27019.sock
-rwxr-xr-x 1 root   root   1042 Sep 13 09:00 passwd
drwx------ 3 root   root     17 Sep 20 07:20 systemd-private-3f6f6b8540bb4821baa29cc8d067029b-chronyd.service-ATfvsa
drwx------ 3 root   root     17 Sep 20 07:21 systemd-private-3f6f6b8540bb4821baa29cc8d067029b-httpd.service-B5HtVK
drwx------ 3 root   root     17 Sep 20 07:20 systemd-private-3f6f6b8540bb4821baa29cc8d067029b-systemd-hostnamed.service-sfZTSG
drwx------ 3 root   root     17 Sep 20 07:23 systemd-private-48497924bb434b52bc8db3b05c607623-chronyd.service-cFxvoE
drwx------ 3 root   root     17 Sep 20 07:23 systemd-private-48497924bb434b52bc8db3b05c607623-httpd.service-PGzvnW
drwx------ 3 root   root     17 Sep 13 08:41 systemd-private-c40d86d5546d46c68cbb031445b13d64-chronyd.service-LsRxBQ
-rwxr-xr-x 1 root   root     39 Sep 13 09:10 test.sh
-rw-r--r-- 1 root   root      0 Sep 20 08:40 when.txt         //多了when.txt文件

playbook中的handlers

我們在命令行下,經常會用到這樣的命令:command1 && command2,這表示command1執行成功後才執行command2,command1若執行失敗,則不執行command2。

playbook中,handlers就類似與符號 && ,起到與它一致的作用。 經常用於在執行task之後,服務器發生變化之後要執行的一些操作。比如在修改了配置文件後,需要重啓一下服務。

  • 編輯一個帶handlers的playbook:
[root@lzx ansible]# vim hand.yml          //寫入下面內容,注意空格
---
- name: handlers test
  hosts: lzx1
  user: root
  tasks:
    - name: copy file
      copy: src=/etc/passwd dest=/tmp/aaa.txt         //copy模塊;src表示源文件,dest表示目標文件
      notify: test handlers        //與handlers關聯,表示接下來要運行handlers,後面指定handlers名字
  handlers:
    - name: test handlers     //定義handlers名字
      shell: echo "111111" >> /tmp/aaa.txt       //shell模塊,執行後面具體操作
  • 執行上面的playbook:
[root@lzx ansible]# ansible-playbook hand.yml 

PLAY [handlers test] ***************************************************************************************

TASK [Gathering Facts] *************************************************************************************
Enter passphrase for key '/root/.ssh/id_rsa':            //輸入生成密鑰時設置的密碼
ok: [lzx1]

TASK [copy file] *******************************************************************************************
changed: [lzx1]

RUNNING HANDLER [test handlers] ****************************************************************************
changed: [lzx1]

PLAY RECAP *************************************************************************************************
lzx1                       : ok=3    changed=2    unreachable=0    failed=0   

到lzx1上查看

[root@lzx1 ~]# tail /tmp/aaa.txt 
dbus:x:81:81:System message bus:/:/sbin/nologin
polkitd:x:999:997:User for polkitd:/:/sbin/nologin
postfix:x:89:89::/var/spool/postfix:/sbin/nologin
chrony:x:998:996::/var/lib/chrony:/sbin/nologin
sshd:x:74:74:Privilege-separated SSH:/var/empty/sshd:/sbin/nologin
admin:x:1000:1000:admin:/home/admin:/bin/bash
mongod:x:997:995:mongod:/var/lib/mongo:/bin/false
tss:x:59:59:Account used by the trousers package to sandbox the tcsd daemon:/dev/null:/sbin/nologin
apache:x:48:48:Apache:/usr/share/httpd:/sbin/nologin
111111          //說明上面handlers執行成功

需要注意的是,上面如果目標文件不存在,則copy時會失敗,進而也不會執行後面的handlers

playbook安裝nginx

在學習了playbook的這些用法後,接下來我們嘗試通過playbook去源碼安裝nginx。

首先在一臺機器上編譯安裝好nginx、打包,然後再用ansible去分發。

  • 創建管理目錄:
[root@lzxble]# mkdir nginx_install        //創建nginx安裝管理目錄
[root@lzx ansible]# cd nginx_install/
[root@lzx nginx_install]# mkdir -p roles/{common,install}/{handlers,files,meta,tasks,templates,vars}          //級聯創建目錄
[root@lzx nginx_install]# ls 
roles
[root@lzx nginx_install]# ls roles/
common  install
[root@lzx nginx_install]# ls roles/common/
files  handlers  meta  tasks  templates  vars
[root@lzx nginx_install]# ls roles/install/
files  handlers  meta  tasks  templates  vars

files存放安裝文件,handler存放handlers文件,meta存放說明信息、說明角色依賴等信息,tasks存放核心配置文件,templates存放配置文件、啓動腳本等模板文件,vars存放定義的變量

如果機器沒有安裝nginx,需要先安裝好才能進行下一步,nginx的安裝步驟請參考這裏:https://blog.csdn.net/miss1181248983/article/details/80890649l ,可以先安裝好pcre-devel和zlib-devel依賴包,否則初始化會報錯,這裏就不再贅述。

  • 打包nginx:
[root@lzx nginx_install]# ls /usr/local/nginx/        //nginx程序主目錄
client_body_temp  conf  fastcgi_temp  html  logs  proxy_temp  sbin  scgi_temp  uwsgi_temp
[root@lzx nginx_install]# ls /etc/init.d/nginx         //nginx啓動腳本
/etc/init.d/nginx
[root@lzx nginx_install]# ls /usr/local/nginx/conf/nginx.conf         //nginx配置文件
/usr/local/nginx/conf/nginx.conf
[root@lzx nginx_install]# cd /usr/local/
[root@lzx local]# tar czvf nginx.tar.gz --exlude "nginx.conf" --exclude "vhost" nginx/       //打包程序主目錄,除配置文件和虛擬主機目錄外
nginx/
nginx/sbin/
nginx/sbin/nginx
nginx/conf/
nginx/conf/koi-win
nginx/conf/koi-utf
nginx/conf/win-utf
nginx/conf/mime.types
nginx/conf/mime.types.default
nginx/conf/fastcgi_params
nginx/conf/fastcgi_params.default
nginx/conf/fastcgi.conf
nginx/conf/fastcgi.conf.default
nginx/conf/uwsgi_params
nginx/conf/uwsgi_params.default
nginx/conf/scgi_params
nginx/conf/scgi_params.default
nginx/conf/nginx.conf.default
nginx/logs/
nginx/logs/error.log
nginx/logs/nginx.pid
nginx/logs/nginx_error.log
nginx/logs/access.log
nginx/html/
nginx/html/50x.html
nginx/html/index.html
nginx/client_body_temp/
nginx/proxy_temp/
nginx/fastcgi_temp/
nginx/uwsgi_temp/
nginx/scgi_temp/
  • 移動打包文件到ansible中nginx對應的安裝管理目錄中:
[root@lzx local]# mv nginx.tar.gz /etc/ansible/nginx_install/roles/install/files/
[root@lzx local]# cp nginx/conf/nginx.conf /etc/ansible/nginx_install/roles/install/templates/
[root@lzx local]# cp /etc/init.d/nginx /etc/ansible/nginx_install/roles/install/templates/

//將nginx目錄放到files下面,將啓動腳本和配置文件放到templates下面
  • 定義common目錄下的tasks,nginx是需要一些依賴包:
[root@lzx local]# cd /etc/ansible/nginx_install/roles/common/
[root@lzx common]# ls
files  handlers  meta  tasks  templates  vars
[root@lzx common]# vim tasks/main.yml         //寫入下面內容,注意空格
- name: install initializtion require software
  yum: name={{ item }} state=installed         //採用循環安裝依賴包
  with_items:
    - pcre-devel
    - zlib-devel
  • 定義變量:
[root@lzx common]# vim /etc/ansible/nginx_install/roles/install/vars/main.yml        //寫入下面內容,注意空格
nginx_user: www
nginx_port: 80
nginx_basedir: /usr/local/nginx        //左邊爲變量名,右邊爲變量的值
  • 拷貝需要用到的文件文檔到目標機器:
[root@lzx common]# vim /etc/ansible/nginx_install/roles/install/tasks/copy.yml       //寫入下面內容,注意空格
- name: Copy Nginx Software
  copy: src=nginx.tar.gz dest=/tmp/nginx.tar.gz owner=root group=root        //copy模塊,拷貝nginx.tar.gz;src寫的是相對路徑,這裏它會自動去files目錄查找對應文件
- name: Uncompression Nginx Software
  shell: tar zxvf /tmp/nginx.tar.gz -C /usr/local         //shell模塊,用來解壓
- name: Copy Nginx Start Script
  template: src=nginx dest=/etc/init.d/nginx owner=root group=root mode=0755       //template模塊,拷貝啓動腳本;src寫的是相對路徑,這裏它會自動去template目錄查找對應文件
- name: Copy Nginx Config      
  template: src=nginx.conf dest={{ nginx_basedir }}/conf/ owner=root group=root mode=0644       //template模塊,拷貝配置文件;src寫的是相對路徑,這裏它會自動去template目錄查找對應文件
  • 建立用戶,啓動服務,刪除壓縮包:
[root@lzx common]# vim /etc/ansible/nginx_install/roles/install/tasks/install.yml      //寫入下面內容,注意空格
- name: Create Nginx User
  user: name={{ nginx_user }} state=present createhome=no shell=/sbin/nologin       //user模塊,創建nginx用戶,定義shell,之前vars裏面已定義用戶
- name: Start Nginx Service
  shell: /etc/init.d/nginx start       //shell模塊,啓動nginx服務
- name: Add Boot Start Nginx Service
  shell: chkconfig --level 345 nginx on        //shell模塊,將nginx服務加入開機啓動,這裏CentOS7也支持該命令
- name: Delete Nginx compression files
  shell: rm -rf /tmp/nginx.tar.gz       //shell模塊,刪除壓縮包
  • 創建main.yml來調用copy.yml和install.yml:
[root@lzx common]# vim /etc/ansible/nginx_install/roles/install/tasks/main.yml      //寫入下面內容,注意空格
- include: copy.yml
- include: install.yml
  • 定義入口配置文件:
[root@lzx common]# vim /etc/ansible/nginx_install/install.yml      //寫入下面內容,注意空格
---      //這裏是總的開頭,上面都是子配置文件,所以不需要加---,但這裏不可省略
- hosts: lzx1      //通常生產環境下,爲一組機器,例如testhost
  remote_user: root       //定義遠程執行用戶
  gather_facts: True        //收集信息
  roles:
    - common
    - install
  • 查看lzx1上是否有/usr/local/nginx目錄:
[root@lzx1 ~]# ls /usr/local/
bin  etc  games  include  lib  lib64  libexec  sbin  share  src
[root@lzx1 ~]# netstat -lntp
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name    
tcp        0      0 192.168.100.160:27019   0.0.0.0:*               LISTEN      869/mongod          
tcp        0      0 127.0.0.1:27019         0.0.0.0:*               LISTEN      869/mongod          
tcp        0      0 0.0.0.0:22              0.0.0.0:*               LISTEN      751/sshd            
tcp        0      0 127.0.0.1:25            0.0.0.0:*               LISTEN      842/master          
tcp6       0      0 :::22                   :::*                    LISTEN      751/sshd            
tcp6       0      0 ::1:25                  :::*                    LISTEN      842/master  

在執行playbook之前,我們必須要保證目標機器上沒有/usr/local/nginx目錄,沒有安裝nginx。同時,80端口必須沒有佔用,否則執行下面的playbook會報錯。

  • 執行入口配置文件:
[root@lzx common]# ansible-playbook /etc/ansible/nginx_install/install.yml        //執行該playbook
PLAY [lzx1] ********************************************************************************************

TASK [Gathering Facts] *********************************************************************************
Enter passphrase for key '/root/.ssh/id_rsa':            //輸入生成密鑰時設置的密碼
ok: [lzx1]

TASK [common : install initializtion require software] *************************************************
ok: [lzx1] => (item=[u'pcre-devel', u'zlib-devel'])

TASK [install : Copy Nginx Software] *******************************************************************
ok: [lzx1]

TASK [install : Uncompression Nginx Software] **********************************************************
 [WARNING]: Consider using the unarchive module rather than running tar.  If you need to use command
because unarchive is insufficient you can add warn=False to this command task or set
command_warnings=False in ansible.cfg to get rid of this message.

changed: [lzx1]

TASK [install : Copy Nginx Start Script] ***************************************************************
ok: [lzx1]

TASK [install : Copy Nginx Config] *********************************************************************
ok: [lzx1]

TASK [install : Create Nginx User] *********************************************************************
ok: [lzx1]

TASK [install : Start Nginx Service] *******************************************************************
changed: [lzx1]

TASK [install : Add Boot Start Nginx Service] **********************************************************
changed: [lzx1]

TASK [install : Delete Nginx compression files] ********************************************************
 [WARNING]: Consider using the file module with state=absent rather than running rm.  If you need to
use command because file is insufficient you can add warn=False to this command task or set
command_warnings=False in ansible.cfg to get rid of this message.

changed: [lzx1]

PLAY RECAP *********************************************************************************************
lzx1                       : ok=10   changed=4    unreachable=0    failed=0         //執行成功

到lzx1上查看:

[root@lzx1 ~]# ps aux |grep nginx
root       2595  0.0  0.0  20556   628 ?        Ss   23:33   0:00 nginx: master process /usr/local/ngin/sbin/nginx -c /usr/local/nginx/conf/nginx.conf
nobody     2597  0.0  0.3  23044  3212 ?        S    23:33   0:00 nginx: worker process
nobody     2598  0.0  0.3  23044  3212 ?        S    23:33   0:00 nginx: worker process
root       2724  0.0  0.0 112704   968 pts/0    R+   23:39   0:00 grep --color=auto nginx
[root@lzx1 ~]# netstat -lntp
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name    
tcp        0      0 192.168.100.160:27019   0.0.0.0:*               LISTEN      869/mongod          
tcp        0      0 127.0.0.1:27019         0.0.0.0:*               LISTEN      869/mongod          
tcp        0      0 0.0.0.0:80              0.0.0.0:*               LISTEN      2595/nginx: master  
tcp        0      0 0.0.0.0:22              0.0.0.0:*               LISTEN      751/sshd            
tcp        0      0 127.0.0.1:25            0.0.0.0:*               LISTEN      842/master          
tcp6       0      0 :::22                   :::*                    LISTEN      751/sshd            
tcp6       0      0 ::1:25                  :::*                    LISTEN      842/master  

[root@lzx1 ~]# date
Fri Sep 21 23:40:50 EDT 2018

目標機器lzx1上已經啓動了nginx服務,並監聽了80端口

playbook管理配置文件

生產環境中大多時候是需要配置文件的,安裝軟件包只是在初始化環境的時候用到。下面來寫個管理nginx配置文件的playbook。

  • 創建管理nginx配置文件的目錄:
[root@lzx common]# mkdir -p /etc/ansible//nginx_config/roles/{new,old}/{files,handlers,vars,tasks}
[root@lzx common]# cd /etc/ansible/
[root@lzx ansible]# ls
ansible.cfg        create_user.yml  hosts         nginx_install  test.yml  while.yml
create_user.retry  hand.yml         nginx_config  roles          when.yml
[root@lzx ansible]# cd nginx_config/
[root@lzx nginx_config]# ls
roles
[root@lzx nginx_config]# ls roles/
new  old
[root@lzx nginx_config]# ls roles/new/
files  handlers  tasks  vars
[root@lzx nginx_config]# ls roles/old/
files  handlers  tasks  vars

new目錄用來更新配置文件,old用來回滾配置文件;files下面爲nginx.conf和vhost目錄,handlers爲重啓nginx服務的命令。另外,關於回滾,需要在更新之前備份舊的配置,要保證new/files下面的配置文件和線上的配置一致
  • 先拷貝nginx.conf和vhost目錄放到files目錄下:
[root@lzx nginx_config]# ls /usr/local/nginx/conf/
fastcgi.conf            koi-utf             nginx.conf           uwsgi_params
fastcgi.conf.default    koi-win             nginx.conf.default   uwsgi_params.default
fastcgi_params          mime.types          scgi_params          vhost
fastcgi_params.default  mime.types.default  scgi_params.default  win-utf

[root@lzx nginx_config]# cd /usr/local/nginx/conf/
[root@lzx conf]# cp -r nginx.conf vhost /etc/ansible/nginx_config/roles/new/files/       
  • 定義變量:
[root@lzx conf]# vim /etc/ansible/nginx_config/roles/new/vars/main.yml       //寫入下面內容,注意空格
nginx_basedir: /usr/local/nginx
  • 定義重新加載nginx服務:
[root@lzx conf]# vim /etc/ansible/nginx_config/roles/new/handlers/main.yml       //寫入下面內容,注意空格
- name: restart nginx
  shell: /etc/init.d/nginx reload        //shell模塊,重新加載nginx服務
  • 定義任務:
[root@lzx conf]# vim /etc/ansible/nginx_config/roles/new/tasks/main.yml

- name: copy conf file
  copy: src={{ item.src }} dest={{ nginx_basedir }}/{{ item.dest }} backup=yes owner=root group=root mode=0644      //copy模塊,拷貝配置文件和vhost目錄;有兩個循環對象,而這裏nginx_basedir是前面定義的變量
  with_items:
    - { src: nginx.conf,dest: conf/nginx.conf }
    - { src: vhost,dest: conf/ }
  notify: restart nginx         //調用handlers,handlers名字是restart nginx
  • 定義入口配置文件:
[root@lzx conf]# vim /etc/ansible/nginx_config/update.yml       //寫入下面內容,注意空格
---
- hosts: lzx1
  user: root
  roles:
    - new
  • 執行入口配置文件:
[root@lzx conf]# ansible-playbook /etc/ansible/nginx_config/update.yml        //執行更新的playbook

PLAY [lzx1] ********************************************************************************************

TASK [Gathering Facts] *********************************************************************************
Enter passphrase for key '/root/.ssh/id_rsa':            //輸入生成密鑰時設置的密碼
ok: [lzx1]

TASK [new : copy conf file] ****************************************************************************
ok: [lzx1] => (item={u'dest': u'conf/nginx.conf', u'src': u'nginx.conf'})
changed: [lzx1] => (item={u'dest': u'conf/', u'src': u'vhost'})

RUNNING HANDLER [new : restart nginx] ******************************************************************
changed: [lzx1]

PLAY RECAP *********************************************************************************************
lzx1                       : ok=3    changed=2    unreachable=0    failed=0       //執行成功

到lzx1上查看

[root@lzx1 ~]# ps aux |grep nginx
root       2595  0.0  0.1  20688  1424 ?        Ss   Sep21   0:00 nginx: master process /usr/local/ngin/sbin/nginx -c /usr/local/nginx/conf/nginx.conf
nobody     3203  0.0  0.3  23176  3324 ?        S    00:39   0:00 nginx: worker process
nobody     3204  0.0  0.3  23176  3324 ?        S    00:39   0:00 nginx: worker process
root       3217  0.0  0.0 112704   968 pts/0    R+   00:43   0:00 grep --color=auto nginx
[root@lzx1 ~]# date
Sat Sep 22 00:43:14 EDT 2018
[root@lzx1 ~]# ls /usr/local/nginx/conf/
fastcgi.conf            koi-utf             nginx.conf           uwsgi_params
fastcgi.conf.default    koi-win             nginx.conf.default   uwsgi_params.default
fastcgi_params          mime.types          scgi_params          vhost
fastcgi_params.default  mime.types.default  scgi_params.default  win-utf
  • 做點變更驗證:
[root@lzx conf]# cd /etc/ansible/nginx_config/roles/new/files/ 
[root@lzx files]# ls
nginx.conf  vhost
[root@lzx files]# vim nginx.conf        //坐下面變更
#       include vhost/*.conf       //註釋這行,前面加上#
[root@lzx files]# ansible-playbook /etc/ansible/nginx_config/update.yml        //執行更新的playbook
PLAY [lzx1] ********************************************************************************************

TASK [Gathering Facts] *********************************************************************************
Enter passphrase for key '/root/.ssh/id_rsa':            //輸入生成密鑰時設置的密碼
ok: [lzx1]

TASK [new : copy conf file] ****************************************************************************
changed: [lzx1] => (item={u'dest': u'conf/nginx.conf', u'src': u'nginx.conf'})
ok: [lzx1] => (item={u'dest': u'conf/', u'src': u'vhost'})

RUNNING HANDLER [new : restart nginx] ******************************************************************
changed: [lzx1]

PLAY RECAP *********************************************************************************************
lzx1                       : ok=3    changed=2    unreachable=0    failed=0        //執行成功

到lzx1上查看

[root@lzx1 ~]# cat /usr/local/nginx/conf/nginx.conf
user nobody nobody;
worker_processes 2;
error_log /usr/local/nginx/logs/nginx_error.log crit;
pid /usr/local/nginx/logs/nginx.pid;
worker_rlimit_nofile 51200;
events
{
    use epoll;
    worker_connections 6000;
}
http
{
    include mime.types;
    default_type application/octet-stream;
    server_names_hash_bucket_size 3526;
    server_names_hash_max_size 4096;
    log_format combined_realip '$remote_addr $http_x_forwarded_for [$time_local]'
    ' $host "$request_uri" $status'
    ' "$http_referer" "$http_user_agent"';
    sendfile on;
    tcp_nopush on;
    keepalive_timeout 30;
    client_header_timeout 3m;
    client_body_timeout 3m;
    send_timeout 3m;
    connection_pool_size 256;
    client_header_buffer_size 1k;
    large_client_header_buffers 8 4k;
    request_pool_size 4k;
    output_buffers 4 32k;
    postpone_output 1460;
    client_max_body_size 10m;
    client_body_buffer_size 256k;
    client_body_temp_path /usr/local/nginx/client_body_temp;
    proxy_temp_path /usr/local/nginx/proxy_temp;
    fastcgi_temp_path /usr/local/nginx/fastcgi_temp;
    fastcgi_intercept_errors on;
    tcp_nodelay on;
    gzip on;
    gzip_min_length 1k;
    gzip_buffers 4 8k;
    gzip_comp_level 5;
    gzip_http_version 1.1;
    gzip_types text/plain application/x-javascript text/css text/htm 
    application/xml;
    server
    {
        listen 80;
        server_name localhost;
        index index.html index.htm index.php;
        root /usr/local/nginx/html;
        location ~ \.php$ 
        {
            include fastcgi_params;
            fastcgi_pass unix:/tmp/php-fcgi.sock;
            fastcgi_index index.php;
            fastcgi_param SCRIPT_FILENAME /usr/local/nginx/html$fastcgi_script_name;
        }    
    }
#	include vhost/*.conf        //這裏也註釋掉了,說明沒問題
}

更新是沒問題的,下面來配置回滾。

回滾操作就是把舊的配置覆蓋,然後重新加載nginx服務,每次改動nginx配置文件之前先備份到old中files目錄裏。

  • 備份配置文件:
[root@lzx files]# rsync -av /etc/ansible/nginx_config/roles/new/ /etc/ansible/nginx_config/roles/old/
sending incremental file list       //拷貝配置文件,-av 保證兩邊完全一致
files/
files/nginx.conf
files/vhost/
files/vhost/default.conf
handlers/
handlers/main.yml
tasks/
tasks/main.yml
vars/
vars/main.yml

sent 2,815 bytes  received 131 bytes  5,892.00 bytes/sec
total size is 2,249  speedup is 0.76
  • 定義入口配置文件:
[root@lzx files]# vim /etc/ansible/nginx_config/rollback.yml         //寫入下面內容,注意空格
---
- hosts: lzx1
  user: root
  roles:
  - old
  • 進行更改:
[root@lzx files]# vim nginx.conf        //做下面更改
        include vhost/*.conf       //去掉前面#
[root@lzx files]# ansible-playbook /etc/ansible/nginx_config/update.yml       //執行更新的playbook

PLAY [lzx1] ********************************************************************************************

TASK [Gathering Facts] *********************************************************************************
Enter passphrase for key '/root/.ssh/id_rsa':            //輸入生成密鑰時設置的密碼
ok: [lzx1]

TASK [new : copy conf file] ****************************************************************************
changed: [lzx1] => (item={u'dest': u'conf/nginx.conf', u'src': u'nginx.conf'})
ok: [lzx1] => (item={u'dest': u'conf/', u'src': u'vhost'})

RUNNING HANDLER [new : restart nginx] ******************************************************************
changed: [lzx1]

PLAY RECAP *********************************************************************************************
lzx1                       : ok=3    changed=2    unreachable=0    failed=0       //執行成功

到lzx1上查看

[root@lzx1 ~]# cat /usr/local/nginx/conf/nginx.conf
user nobody nobody;
worker_processes 2;
error_log /usr/local/nginx/logs/nginx_error.log crit;
pid /usr/local/nginx/logs/nginx.pid;
worker_rlimit_nofile 51200;
events
{
    use epoll;
    worker_connections 6000;
}
http
{
    include mime.types;
    default_type application/octet-stream;
    server_names_hash_bucket_size 3526;
    server_names_hash_max_size 4096;
    log_format combined_realip '$remote_addr $http_x_forwarded_for [$time_local]'
    ' $host "$request_uri" $status'
    ' "$http_referer" "$http_user_agent"';
    sendfile on;
    tcp_nopush on;
    keepalive_timeout 30;
    client_header_timeout 3m;
    client_body_timeout 3m;
    send_timeout 3m;
    connection_pool_size 256;
    client_header_buffer_size 1k;
    large_client_header_buffers 8 4k;
    request_pool_size 4k;
    output_buffers 4 32k;
    postpone_output 1460;
    client_max_body_size 10m;
    client_body_buffer_size 256k;
    client_body_temp_path /usr/local/nginx/client_body_temp;
    proxy_temp_path /usr/local/nginx/proxy_temp;
    fastcgi_temp_path /usr/local/nginx/fastcgi_temp;
    fastcgi_intercept_errors on;
    tcp_nodelay on;
    gzip on;
    gzip_min_length 1k;
    gzip_buffers 4 8k;
    gzip_comp_level 5;
    gzip_http_version 1.1;
    gzip_types text/plain application/x-javascript text/css text/htm 
    application/xml;
    server
    {
        listen 80;
        server_name localhost;
        index index.html index.htm index.php;
        root /usr/local/nginx/html;
        location ~ \.php$ 
        {
            include fastcgi_params;
            fastcgi_pass unix:/tmp/php-fcgi.sock;
            fastcgi_index index.php;
            fastcgi_param SCRIPT_FILENAME /usr/local/nginx/html$fastcgi_script_name;
        }    
    }
	include vhost/*.conf        //前面的#消失
}

假如上面更新的操作是有問題的操作,那麼我們需要回滾。

  • 執行回滾的playbook:
[root@lzx files]# ansible-playbook /etc/ansible/nginx_config/rollback.yml        //執行回滾的playbook

PLAY [lzx1] ********************************************************************************************

TASK [Gathering Facts] *********************************************************************************
Enter passphrase for key '/root/.ssh/id_rsa':            //輸入生成密鑰時設置的密碼
ok: [lzx1]

TASK [old : copy conf file] ****************************************************************************
changed: [lzx1] => (item={u'dest': u'conf/nginx.conf', u'src': u'nginx.conf'})
ok: [lzx1] => (item={u'dest': u'conf/', u'src': u'vhost'})

RUNNING HANDLER [old : restart nginx] ******************************************************************
changed: [lzx1]

PLAY RECAP *********************************************************************************************
lzx1                       : ok=3    changed=2    unreachable=0    failed=0      //執行成功

到lzx1上查看

[root@lzx1 ~]# cat /usr/local/nginx/conf/nginx.conf
user nobody nobody;
worker_processes 2;
error_log /usr/local/nginx/logs/nginx_error.log crit;
pid /usr/local/nginx/logs/nginx.pid;
worker_rlimit_nofile 51200;
events
{
    use epoll;
    worker_connections 6000;
}
http
{
    include mime.types;
    default_type application/octet-stream;
    server_names_hash_bucket_size 3526;
    server_names_hash_max_size 4096;
    log_format combined_realip '$remote_addr $http_x_forwarded_for [$time_local]'
    ' $host "$request_uri" $status'
    ' "$http_referer" "$http_user_agent"';
    sendfile on;
    tcp_nopush on;
    keepalive_timeout 30;
    client_header_timeout 3m;
    client_body_timeout 3m;
    send_timeout 3m;
    connection_pool_size 256;
    client_header_buffer_size 1k;
    large_client_header_buffers 8 4k;
    request_pool_size 4k;
    output_buffers 4 32k;
    postpone_output 1460;
    client_max_body_size 10m;
    client_body_buffer_size 256k;
    client_body_temp_path /usr/local/nginx/client_body_temp;
    proxy_temp_path /usr/local/nginx/proxy_temp;
    fastcgi_temp_path /usr/local/nginx/fastcgi_temp;
    fastcgi_intercept_errors on;
    tcp_nodelay on;
    gzip on;
    gzip_min_length 1k;
    gzip_buffers 4 8k;
    gzip_comp_level 5;
    gzip_http_version 1.1;
    gzip_types text/plain application/x-javascript text/css text/htm 
    application/xml;
    server
    {
        listen 80;
        server_name localhost;
        index index.html index.htm index.php;
        root /usr/local/nginx/html;
        location ~ \.php$ 
        {
            include fastcgi_params;
            fastcgi_pass unix:/tmp/php-fcgi.sock;
            fastcgi_index index.php;
            fastcgi_param SCRIPT_FILENAME /usr/local/nginx/html$fastcgi_script_name;
        }    
    }
#	include vhost/*.conf          //可以看到,前面有#,已經回滾爲之前的版本
}

能夠進行回滾操作的關鍵是,在更新之前做一次備份,使用rsync -av 能夠保證兩邊配置一致,再進行更新。如果更新出現問題,這時我們在執行回滾操作即可。


更多資料參考:
Ansible之playbook的使用
Ansible 系列之 Playbooks 劇本(1)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章