php-cgi进程过多,导致系统资源耗尽

Posted 散尽浮华

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了php-cgi进程过多,导致系统资源耗尽相关的知识,希望对你有一定的参考价值。

 

之前详细介绍了Ansible的安装, 配置, 以及Ansible常用模块的使用. 下面对Ansible的playbook用法做一小结。

为什么引入playbook?
一般运维人员完成一个任务, 比如安装部署一个httpd服务会需要多个模块(一个模块也可以称之为task)提供功能来完成。而playbook就是组织多个task的容器,它的实质就是一个文件,有着特定的组织格式,它采用的语法格式是YAML(Yet Another Markup Language)。YAML语法能够简单的表示散列表,字典等数据结构。简单来说, playbook是由一个或多个模块组成的,使用多个不同的模块,完成一件事情。 

Ansible核心功能
-  pyYAML用于ansible编写剧本所使用的语言格式(saltstack---python);
-  rsync-ini语法, sersync-xml语法, nsible-pyYAML语法;
-  paramiko远程连接与数据传输;
-  Jinja2用于编写ansible的模板信息;

YAML三板斧
缩进: YAML使用一个固定的缩进风格表示层级结构,每个缩进由两个空格组成, 不能使用tabs;
冒号: 以冒号结尾的除外,其他所有冒号后面所有必须有空格;
短横线: 表示列表项,使用一个短横杠加一个空格。多个项使用同样的缩进级别作为同一列表;

YAML基本语法
Ansible-playbook采用YAML语法编写。连续的项目(即列表)用 -减号来表示,key/value(字典)用冒号:分隔。

列表:每一个列表成员前面都要有一个短横线和一个空格

fruits:
    - Apple
    - Orange
    - Strawberry
    - Mango

或者:
fruits: [\'Apple\', \'Orange\', \'Strawberry\', \'Mango\']

字典:每一个成员由键值对组成,注意冒号后面要有空格

martin:
    name: Martin D\'vloper
    job: Developer
    skill: Elite
或者
martin: {name: Martin D\'vloper, job: Developer, skill: Elite}

列表和字典可以混合使用

-  martin:
    name: Martin D\'vloper
    job: Developer
    skills:
      - python
      - perl
      - pascal
-  tabitha:
    name: Tabitha Bitumen
    job: Developer
    skills:
      - lisp
      - fortran
      - erlang

示例如下:
[root@localhost ~]# cat httpd.yaml

---
- hosts: control-node    #将要执行任务的主机,已经在hosts文件中定义好了,可是单个主机或主机组
  remote_user: root      #在目标主机上执行任务时的用户身份
  vars:
    - pkg: httpd
  tasks:
    - name: "install httpd package."
      yum: name={{ pkg }}  state=installed
    - name: "copy httpd configure file to remote host."
      copy: src=/root/conf/httpd.conf dest=/etc/httpd/conf/httpd.conf
      notify: restart httpd     #当这个任务执行状态发生改变时,触发handlers执行.
    - name: "boot httpd service."
      service: name=httpd state=started
  handlers:                     #handlers与tasks是同一级别
    - name: restart httpd
      service: name=httpd state=restarted

playbook语法特性
1.  以 --- (三个减号)开始,必须顶行写;
2. 次行开始写Playbook的内容,但是一般要求写明该playbook的功能;
3. 严格缩进,并且不能用Tab键缩进;
4. 缩进级别必须是一致的,同样的缩进代表同样的级别,程序判别配置的级别是通过缩进结合换行来实现的;
5. K/V的值可同行写,也可换行写。同行使用 :分隔,换行写需要以 - 分隔;

playbook基础组件
Hosts:运行执行任务(task)的目标主机
remote_user:在远程主机上执行任务的用户
tasks:任务列表
handlers:任务,与tasks不同的是只有在接受到通知时才会被触发
templates:使用模板语言的文本文件,使用jinja2语法。
variables:变量,变量替换{{ variable_name }}

整个playbook是以task为中心,表明要执行的任务。hosts和remote_user表明在远程主机以何种身份执行,其他组件让其能够更加灵活。下面介绍插件:

1.  variable
变量定义在资产 (inventory) 中, 默认就是/etc/ansible/hosts文件中

主机变量:
192.168.200.136 http_port=808 maxRequestsPerChild=808
192.168.200.137 http_port=8080 maxRequestsPerChild=909

主机组变量:
[websers]
192.168.200.136
192.168.200.137

[websers:vars]  
ntp_server=ntp.exampl.com
proxy=proxy.exampl.com

变量定义在playbook中

- hosts: webservers
  vars:
    http_port: 80

使用facts变量

facts变量是由setup模块获取远程主机的信息。

用法:
# ansible 192.168.200.136 -m setup

在roles中定义变量, 这个后面会介绍到.

ansible-playbook 命令中传入参数

使用 -e选项传入参数
# ansible-playbook 192.168.200.136 -e "httpd_port=808" httpd04.yml

变量的引用

{{ var_name }}

2.  templates
它是一个模块功能,与copy不同的是他的文本文件采用了jinga2语法,jinga2基本语法如下:

字面量:
  字符串:使用单引号或双引号
  数字:整型,浮点数
  列表:{item1,item2,...}
  字典:{key1:value1,key2:value2,...}
  布尔型:true/false
算术运算:
  +,-,*,/,//,%,**
比较运算:
  ==,!=,>,>=,<,<=
逻辑运算:
  and,or,not

注意:template只能在palybook中使用。

3.  tasks
执行的模块命令

格式:
  action:模块参数(此种方式只在较新的版本中出现)
  module:参数(已键值对的形式出现)

每一个task都有一个名称,用于标记此任务。任务示例:
  name: install httpd
  yum: name=httpd state=present

注意:shell和command没有参数,可在后面直接跟命令
  shell: ss -tnl | grep :80

1)某任务的运行状态为changed后,可通过相应的notify通知相应的handlers
2)任务可以通过tags打标签,然后通过palybook命令-t选项调用.

playbook命令及调用方式

用法:
ansible-playbook  <filename.yml> ... [options]

<filename.yml>: yaml格式的playbook文件路径,必须指明
[options]: 选项

Options:
--ask-vault-pass     
             #ask for vault password
             #加密playbook文件时提示输入密码
  -C, --check          
             #don\'t make any changes; instead, try to predict some of the changes that may occur
             #模拟执行,不会真正在机器上执行(查看执行会产生什么变化)。即并不在远程主机上执行,只是测试。
  -D, --diff           
             #when changing (small) files and templates, show the differences in those files; works great with --check
             #当更新的文件数及内容较少时,该选项可显示这些文件不同的地方,该选项结合-C用会有较好的效果
  -e EXTRA_VARS, --extra-vars=EXTRA_VARS
             #set additional variables as key=value or YAML/JSON
             #在Playbook中引入外部参数变量
  --flush-cache        
             #clear the fact cache
             #清理fact缓存,将fact清除到的远程主机缓存
  --force-handlers     
             #run handlers even if a task fails
             #强制运行handlers的任务,即使在任务失败的情况下
  -f FORKS, --forks=FORKS
             #specify number of parallel processes to use(default=5)
             #并行任务数。FORKS被指定为一个整数,默认是5
  -h, --help           
             #show this help message and exit
             #打开帮助文档API
  -i INVENTORY, --inventory-file=INVENTORY
             #specify inventory host path (default=/etc/ansible/hosts) or comma separated host list.
             #指定要读取的Inventory清单文件
  -l SUBSET, --limit=SUBSET
             #further limit selected hosts to an additional pattern
             #限定执行的主机范围
  --list-hosts         
             #outputs a list of matching hosts; does not execute anything else
             #列出执行匹配到的主机,但并不会执行任何动作。
  --list-tags          
             #list all available tags
             #列出所有可用的tags
  --list-tasks         
             #list all tasks that would be executed
             #列出所有即将被执行的任务
  -M MODULE_PATH, --module-path=MODULE_PATH
             #specify path(s) to module library (default=None)
             #要执行的模块的路径
  --new-vault-password-file=NEW_VAULT_PASSWORD_FILE
             #new vault password file for rekey
             #
  --output=OUTPUT_FILE 
             #output file name for encrypt or decrypt; use - for stdout
             #
  --skip-tags=SKIP_TAGS
             #only run plays and tasks whose tags do not match these values
             #跳过指定的tags任务
  --start-at-task=START_AT_TASK
             #start the playbook at the task matching this name
             #从第几条任务(START_AT_TASK)开始执行
  --step               
             #one-step-at-a-time: confirm each task before running
             #逐步执行Playbook定义的任务,并经人工确认后继续执行下一步任务
  --syntax-check       
             #perform a syntax check on the playbook, but do not execute it
             #检查Playbook中的语法书写,并不实际执行
  -t TAGS, --tags=TAGS 
             #only run plays and tasks tagged with these values
             #指定执行该tags的任务
  --vault-password-file=VAULT_PASSWORD_FILE
             #vault password file
             #
  -v, --verbose        
             #verbose mode (-vvv for more, -vvvv to enable connection debugging)
             #执行详细输出
  --version            
             #show program\'s version number and exit
             #显示版本
 
  ############Connection Options,即下面时连接权限############
    control as whom and how to connect to hosts
 
    -k, --ask-pass     
             #ask for connection password
             #
    --private-key=PRIVATE_KEY_FILE, --key-file=PRIVATE_KEY_FILE
             #use this file to authenticate the connection
             #
    -u REMOTE_USER, --user=REMOTE_USER
             #connect as this user (default=None)
             #指定远程主机以USERNAME运行命令
    -c CONNECTION, --connection=CONNECTION
             #connection type to use (default=smart)
             #指定连接方式,可用选项paramiko (SSH)、ssh、local,local方式常用于crontab和kickstarts
    -T TIMEOUT, --timeout=TIMEOUT
             #override the connection timeout in seconds(default=10)
             #SSH连接超时时间设定,默认10s
    --ssh-common-args=SSH_COMMON_ARGS
             #specify common arguments to pass to sftp/scp/ssh (e.g.ProxyCommand)
             #
    --sftp-extra-args=SFTP_EXTRA_ARGS
             #specify extra arguments to pass to sftp only (e.g. -f, -l)
             #
    --scp-extra-args=SCP_EXTRA_ARGS
             #specify extra arguments to pass to scp only (e.g. -l)
             #
    --ssh-extra-args=SSH_EXTRA_ARGS
             #specify extra arguments to pass to ssh only (e.g. -R)
             #
 
  ############Privilege Escalation Options, 即下面时权限提升权限############
    control how and which user you become as on target hosts
 
    -s, --sudo         
             #run operations with sudo (nopasswd) (deprecated, use become)
             #相当于Linux系统下的sudo命令
    -U SUDO_USER, --sudo-user=SUDO_USER
             #desired sudo user (default=root) (deprecated, use become)
             #使用sudo,相当于Linux下的sudo命令
    -S, --su           
             #run operations with su (deprecated, use become)
             #
    -R SU_USER, --su-user=SU_USER
             #run operations with su as this user (default=root)(deprecated, use become)
    -b, --become       
             #run operations with become (does not imply password prompting)
             #
    --become-method=BECOME_METHOD
             #privilege escalation method to use (default=sudo),valid choices: [ sudo | su | pbrun | pfexec | doas |dzdo | ksu | runas ]
             #
    --become-user=BECOME_USER
             #run operations as this user (default=root)
             #
    --ask-sudo-pass    
             #ask for sudo password (deprecated, use become)
             #传递sudo密码到远程主机,来保证sudo命令的正常运行
    --ask-su-pass      
             #ask for su password (deprecated, use become)
             #
    -K, --ask-become-pass
             #ask for privilege escalation password
             #

ansible-playbook需要注意的两个命令
1)检查语法,只检查是否是yaml语法格式。并不做逻辑校验。(记住这个要经常使用, 它是判断语法是否正确!!!)
# ansible-playbook --syntax-check kevin.yml
2)模拟执行(不是真的执行)
# ansible-playbook -C kevin.yml

关闭Facts
如果不需要使用主机的任何fact数据,可以选择关闭fact数据的获取,这样有利于增强Ansible面对大量系统的push模块。
在playbook中关闭Facts方法(gather_facts: no):

---
- hosts: webserver
  gather_facts: no

palybook书写格式

---                                   # 也可以不使用这一行。可以省略。
- hosts: 172.16.60.211                #处理指定服务器.   - (空格)hosts:(空格)172.16.20.211
  task:                               #剧本所要干的事情; (空格)(空格)task:
  - name:                             #(两个空格)-(空格)name。
    command: echo hello clsn linux    #(四个空格)command:(空格) 

需要注意:
Task任务里的name可以省略不写,将-(空格)放到下一行模块墙面。例如:
---                                   
- hosts: 172.16.60.211               
  task:                             
  - command: echo hello clsn linux

小示例:
[root@localhost ansible]# cat haha.yaml 
---
- hosts: test_host
  remote_user: root
  gather_facts: no
  tasks:
     - file: path=/opt/task1.txt state=touch

palybook格式示例

[root@ansible-server ~]# vim /etc/ansible/test.yaml
- hosts: 172.16.60.213
  tasks:
    - name: Install Rsync
      yum: name=rsync state=installed

playbook检查方法
[root@ansible-server ~]# ansible-playbook --syntax-check /etc/ansible/test.yaml

playbook: /etc/ansible/test.yaml
[root@ansible-server ~]# ansible-playbook -C /etc/ansible/test.yaml

PLAY [172.16.60.213] *******************************************************************************************************************

TASK [Gathering Facts] *****************************************************************************************************************
ok: [172.16.60.213]

TASK [Install Rsync] *******************************************************************************************************************
ok: [172.16.60.213]

PLAY RECAP *****************************************************************************************************************************
172.16.60.213              : ok=2    changed=0    unreachable=0    failed=0  

上面两个检查命令, 第一个是进行playbook剧本配置信息语法检查; 第二个是模拟playbook剧本执行(彩排)

palybook剧本文件示例

ansible-playbook编写内容扩展剧本任务编写多个任务

- hosts: all
  tasks:
    - name: restart-network
      cron: name=\'restart network\' minute=00 hour=00 job=\'/usr/sbin/ntpdate time.nist.gov >/dev/null 2>&1\'
    - name: sync time
      cron: name=\'sync time\' minute=*/5 job="/usr/sbin/ntpdate pool.ntp.com >/dev/null 2>&1"

剧本编写内容扩展:剧本任务编写多个主机

- hosts: 172.16.60.7
  tasks:
    - name: restart-network
      cron: name=\'restart network\' minute=00 hour=00 job=\'/usr/sbin/ntpdate time.nist.gov >/dev/null 2>&1\'
    - name: sync time
      cron: name=\'sync time\' minute=*/5 job="/usr/sbin/ntpdate pool.ntp.com >/dev/null 2>&1"

- hosts: 172.16.60.31
  tasks:
    - name: show ip addr to file
      shell: echo $(hostname -i) >> /tmp/ip.txt

playbook剧本编写方式
-  多主机单任务编写方式
-  多主机多任务编写方式
-  不同主机多任务编写方式

来看一个比较完整的ansible的yml文件写法:

---
- host: webservers       ###要管理的远程服务器组名称, 服务器地址维护在/etc/ansible/hosts 里, 也可以直接写地址
  vars:
    port: 8081           ###定义了一个变量 端口号
  remote_user: root      ###远程登录后用什么用户执行
 
  pre_tasks:             ###执行正式 task 之前执行的任务
  - name: pre task       ###任务名称
    shell: echo \'execute pre task\'      ###执行一行 shell 命令, 支持 >> 等符号
 
  roles:            ###引入 roles, 可以理解为引用了一个其他项目 ansible 包, 引用的 roles 可以是另一个完整的 ansible 脚本
  - role: my_role   ###要引用的 role 名称
    when: "ansible_os_family == \'RedHat\'"   ###判断条件, ansible_os_family 是一个内置变量, 可直接使用
 
  tasks:           ###按顺序执行以下 task
  - include: my_tasks/some_task.yml       ###可以引入其他 yml 文件
  - name: get hostname        ###这是一个 task, 名称
    command: cat log.log      ###执行一行 command , 和 shell 类似, 但是不支持 >> 等操作符
    register: result          ###执行的结果, 设到 result 这个变量中, 后面可以使用
  - name: set hostname 
    shell: cat {{result.stdout}} >> host.text
  - name: task1
    command: echo \'execute task1\'
  - name: task2 start apache
    service:             ###启动 httpd 服务器, service 是一个 ansible 内置模块, 读者可以自行查看更多模块, 包括下载复制等等
      name: httpd
      state: started
      tags:
        - apache        ###这是一个标签, 可以用 ansible-playbook main.yml --tags "apache" 指定只执行这个任务
  - name: copy and set value of index.html
    template:           ###这是一个复制方法, 也叫模块, 并且.j2文件中可以使用{{}}来设置需要替换的变量 
      src: templates/index.html.j2
      dest: /etc/httpd/index.html
    notify:             ###唤醒执行后面的 handlers 中名字叫 restart apache 的任务
    - restart apache
 
  post_tasks:           ###最后需要执行的任务
  - name: posy task
    shell: echo \'execute post task\'
 
  handlers:
  - name: restart apache
    debug:              ###这是一个打印模块
      msg: start restart apche

palybook剧本中的方法

1.  handlers 任务触发
在需要被监控的任务(tasks)中定义一个notify,只有当这个任务被执行时,才会触发notify对应的handlers去执行相应操作。例如配置文件被修改后,有可能需要重启程序,此时我们可以配置一个handlers,类似触发器。注意:handlers下的name名称必须要和它对应的notify名称相同!否则不会执行!!

[root@localhost ~]# cat httpd.yaml 
---
- hosts: control-node
  remote_user: root
  vars:
    - pkg: httpd
  tasks:
    - name: "install httpd package."
      yum: name={{ pkg }}  state=installed
    - name: "copy httpd configure file to remote host."
      copy: src=/root/conf/httpd.conf dest=/etc/httpd/conf/httpd.conf
      notify: restart httpd
    - name: "boot httpd service."
      service: name=httpd state=started
  handlers:
    - name: restart httpd
      service: name=httpd state=restarted

########  在使用handlers的过程中,需要注意下面几点  ########
1. handlers只有在其所在的任务被执行完时,它才会被运行;如果一个任务中定义了notify调用Handlers,但由于条件判断等原因,该任务未被执行,则Handlers同样不会被执行。
2. handlers只会在Play的末尾运行一次;如果想在一个Playbook的中间运行handlers,则需要使用meta模块来实现,例如:-meta: flush_handlers。
3. 可以直接在Handlers中使用notify选项,实现Handlers调用Handlers。
4. 可以使用listen关键字,在一个tasks任务中一次性notify多个handler。即将多个handler分为"一组",使用相同的"组名"即可,当notify对应的值为"组名"时,"组"内的所有handler都会被notify。
5. 如果一个Play在运行到调用handlers的语句之前失败了,那么这个handlers将不会被执行。但是可以使用mega模块的--force-handlers选项来强制执行handlers,即使在handlers所在Play中途运行失败也能执行。需要注意:--force-handlers参数主要针对即使playbook执行失败,也要执行代码块成功了的handlers(即执行成功的task任务), 如果代码块本身执行失败(即执行失败的task任务),那么它所对应的handlers应当不会被执行!

handlers可以理解成另一种tasks,handlers是另一种"任务列表",可以理解handlers和tasks是"平级关系",所以他们的缩进相同。handlers的任务会被tasks中的任务进行"调用",但是,被"调用"并不意味着一定会执行,只有当tasks中的任务"真正执行"以后,handlers中被调用的任务才会执行,如果tasks中的任务并没有做出任何实际的操作,那么handlers中的任务即使被"调用",也并不会执行。handlers中可以有多个任务,被tasks中不同的任务notify。

场景1:headlers在所有tasks任务被执行完时才执行。

[root@localhost ansible]# cat haha.yaml
---
- hosts: test_host
  remote_user: root
  become: yes
  become_method: sudo
  tasks:
    - name: make file task1
      file: path=/opt/task1.txt state=touch
      notify: task1
    - name: make file task2
      file: path=/opt/task2.txt state=touch
      notify: task2
  handlers:
    - name: task1
      file: path=/opt/task1.txt mode=777 owner=root group=root
    - name: task2
      file: path=/opt/task2.txt src=/opt/task2.txt dest=/opt/heihei state=link force=yes

执行结果:
[root@localhost ansible]# ansible-playbook haha.yaml 

PLAY [test_host] *********************************************************************************************************************************

TASK [Gathering Facts] ***************************************************************************************************************************
ok: [172.16.60.233]
ok: [172.16.60.234]

TASK [make file task1] ***************************************************************************************************************************
changed: [172.16.60.234]
changed: [172.16.60.233]

TASK [make file task2] ***************************************************************************************************************************
changed: [172.16.60.233]
changed: [172.16.60.234]

RUNNING HANDLER [task1] **************************************************************************************************************************
changed: [172.16.60.234]
changed: [172.16.60.233]

RUNNING HANDLER [task2] **************************************************************************************************************************
changed: [172.16.60.233]
changed: [172.16.60.234]

PLAY RECAP ***************************************************************************************************************************************
172.16.60.233              : ok=5    changed=4    unreachable=0    failed=0   
172.16.60.234              : ok=5    changed=4    unreachable=0    failed=0

从上面运行结果看出,Handlers执行的顺序与Handlers在playbook中定义的顺序是相同的,与"handler"被notify的顺序无关。

场景2:使用meta模块,headlers会在它所对应的task任务执行完后立即被触发并执行,即在playbook的中间环节运行。
默认情况下,所有的task执行完毕后,才会执行各个handles,并不是执行完某个task后,立即执行相应的handler,如果想要在执行完某些task以后立即执行对应的handlre,那么需要使用meta模块。

[root@localhost ansible]# cat haha.yaml
---
- hosts: test_host
  remote_user: root
  become: yes
  become_method: sudo
  tasks:
    - name: make file task1
      file: path=/opt/task1.txt state=touch
      notify: task1
    - meta: flush_handlers
    - name: make file task2
      file: path=/opt/task2.txt state=touch
      notify: task2
  handlers:
    - name: task1
      file: path=/opt/task1.txt mode=777 owner=root group=root
    - name: task2
      file: path=/opt/task2.txt src=/opt/task2.txt dest=/opt/heihei state=link force=yes

执行结果:
[root@localhost ansible]# ansible-playbook haha.yaml 

PLAY [test_host] *********************************************************************************************************************************

TASK [Gathering Facts] ***************************************************************************************************************************
ok: [172.16.60.234]
ok: [172.16.60.233]

TASK [make file task1] ***************************************************************************************************************************
changed: [172.16.60.234]
changed: [172.16.60.233]

RUNNING HANDLER [task1] **************************************************************************************************************************
changed: [172.16.60.234]
changed: [172.16.60.233]

TASK [make file task2] ***************************************************************************************************************************
changed: [172.16.60.234]
changed: [172.16.60.233]

RUNNING HANDLER [task2] **************************************************************************************************************************
changed: [172.16.60.234]
changed: [172.16.60.233]

PLAY RECAP ***************************************************************************************************************************************
172.16.60.233              : ok=5    changed=4    unreachable=0    failed=0   
172.16.60.234              : ok=5    changed=4    unreachable=0    failed=0 

上面使用了meta模块后,注意它的执行顺序于场景1做下对比!

场景3:Handlers调用Handlers
若实现Handlers调用Handlers,则直接在Handlers中使用notify选项即可以。

[root@localhost ansible]# cat haha.yaml
---
- hosts: test_host
  remote_user: root
  become: yes
  become_method: sudo
  tasks:
    - name: make file task1
      file: path=/opt/task1.txt state=touch
      notify: task1
    - name: make file task2
      file: path=/opt/task2.txt state=touch
       
  handlers:
    - name: task1
      file: path=/opt/task1.txt mode=777 owner=root group=root
      notify: task2
    - name: task2
      file: path=/opt/task2.txt src=/opt/task2.txt dest=/opt/heihei state=link force=yes
 
执行结果:
[root@localhost ansible]# ansible-playbook haha.yaml             
 
PLAY [test_host] *********************************************************************************************************************************
 
TASK [Gathering Facts] ***************************************************************************************************************************
ok: [172.16.60.234]
ok: [172.16.60.233]
 
TASK [make file task1] ***************************************************************************************************************************
changed: [172.16.60.233]
changed: [172.16.60.234]
 
TASK [make file task2] ***************************************************************************************************************************
changed: [172.16.60.234]
changed: [172.16.60.233]
 
RUNNING HANDLER [task1] **************************************************************************************************************************
changed: [172.16.60.234]
changed: [172.16.60.233]
 
RUNNING HANDLER [task2] **************************************************************************************************************************
changed: [172.16.60.234]
changed: [172.16.60.233]
 
PLAY RECAP ***************************************************************************************************************************************
172.16.60.233              : ok=5    changed=4    unreachable=0    failed=0  
172.16.60.234              : ok=5    changed=4    unreachable=0    failed=0


注意:上面执行的顺序是:make file task1 > make file task2 > task1 > task2

====================================================================
也可以改成下面的方式:实现Handlers调用Handlers

[root@localhost ansible]# cat haha.yaml 
---
- hosts: test_host
  remote_user: root
  become: yes
  become_method: sudo
  tasks:
    - name: make file task1
      file: path=/opt/task1.txt state=touch
      notify: task1

  handlers:
    - name: task1
      file: path=/opt/task1.txt mode=777 owner=root group=root
      notify: task2
    - name: task2
      file: path=/opt/task2.txt state=touch
      notify: task3
    - name: task3
      file: path=/opt/task2.txt src=/opt/task2.txt dest=/opt/heihei state=link force=yes

执行结果:
[root@localhost ansible]# ansible-playbook haha.yaml 

PLAY [test_host] *********************************************************************************************************************************

TASK [Gathering Facts] ***************************************************************************************************************************
ok: [172.16.60.233]
ok: [172.16.60.234]

TASK [make file task1] ***************************************************************************************************************************
changed: [172.16.60.233]
changed: [172.16.60.234]

RUNNING HANDLER [task1] **************************************************************************************************************************
changed: [172.16.60.233]
changed: [172.16.60.234]

RUNNING HANDLER [task2] **************************************************************************************************************************
changed: [172.16.60.234]
changed: [172.16.60.233]

RUNNING HANDLER [task3] **************************************************************************************************************************
changed: [172.16.60.234]
changed: [172.16.60.233]

PLAY RECAP ***************************************************************************************************************************************
172.16.60.233              : ok=5    changed=4    unreachable=0    failed=0   
172.16.60.234              : ok=5    changed=4    unreachable=0    failed=0  

注意:上面的执行顺序是:make file task1 > task1 > task2 > task3

场景4:使用listen关键字,在一个tasks任务中一次性notify多个handler
怎么才能一次性notify多个handler呢?如果尝试将多个handler使用相同的name呢?其实这样并不可行!因为当多个handler的name相同时,只有一个handler会被执行。要想实现一次notify多个handler,需要借助一个关键字,它就是"listen",可以把listen理解成"组名",可以把多个handler分成"组",当需要一次性notify多个handler时,只要将多个handler分为"一组",使用相同的"组名"即可,当notify对应的值为"组名"时,"组"内的所有handler都会被notify。需要注意:listen的名称要和notify名称保持一致!

[root@localhost ansible]# cat haha.yaml
---
- hosts: test_host
  remote_user: root
  become: yes
  become_method: sudo
  tasks:
    - name: make file task1
      file: path=/opt/task1.txt state=touch
      notify: group1_handler
  handlers:
    - name: task1
      listen: group1_handler
      file: path=/opt/task1.txt mode=777 owner=root group=root
    - name: task2
      listen: group1_handler
      file: path=/opt/task1.txt src=/opt/task1.txt dest=/opt/heihei state=link force=yes
    - name: task3
      listen: group1_handler
      shell: echo "this is test,haha...." >> /opt/task1.txt

执行结果:
[root@localhost ansible]# ansible-playbook haha.yaml 

PLAY [test_host] *********************************************************************************************************************************

TASK [Gathering Facts] ***************************************************************************************************************************
ok: [172.16.60.233]
ok: [172.16.60.234]

TASK [make file task1] ***************************************************************************************************************************
changed: [172.16.60.234]
changed: [172.16.60.233]

RUNNING HANDLER [task1] **************************************************************************************************************************
changed: [172.16.60.234]
changed: [172.16.60.233]

RUNNING HANDLER [task2] **************************************************************************************************************************
changed: [172.16.60.234]
changed: [172.16.60.233]

RUNNING HANDLER [task3] **************************************************************************************************************************
changed: [172.16.60.234]
changed: [172.16.60.233]

PLAY RECAP ***************************************************************************************************************************************
172.16.60.233              : ok=5    changed=4    unreachable=0    failed=0   
172.16.60.234              : ok=5    changed=4    unreachable=0    failed=0

场景5:使用--force-handlers选项来强制执行handlers
当playbook剧本执行失败以后,handlers可能并没有被触发,也就不会执行了!如果想不管task任务是否成功执行,都强制执行handlers。在这个时候,可以在执行playbook的时候,添加--force-handlers来强制执行handlers!但是必须要注意的是:--force-handlers参数主要针对即使playbook执行失败,也要执行代码块成功了的handlers(即执行成功的task任务), 如果代码块本身执行失败(即执行失败的task任务),那么它所对应的handlers应当不会被执行!

[root@localhost ansible]# cat haha.yaml 
---
- hosts: test_host
  remote_user: root
  become: yes
  become_method: sudo
  tasks:
    - name: make file task1
      file: path=/opt/task1.txt state=touch
      notify: task1
    - name: make file task2
      file: path=/opt/kevin/task2.txt state=touch
      notify: task2
  handlers:
    - name: task1
      file: path=/opt/task1.txt mode=777 owner=root group=root
    - name: task2
      shell: ln -s /opt/task1.txt /opt/task2.txt

执行结果:
[root@localhost ansible]# ansible-playbook haha.yaml 

PLAY [test_host] *********************************************************************************************************************************

TASK [Gathering Facts] ***************************************************************************************************************************
ok: [172.16.60.234]
ok: [172.16.60.233]

TASK [make file task1] ***************************************************************************************************************************
changed: [172.16.60.233]
changed: [172.16.60.234]

TASK [make file task2] ***************************************************************************************************************************
fatal: [172.16.60.234]: FAILED! => {"changed": false, "module_stderr": "", "module_stdout": "Traceback (most recent call last):\\r\\n  File \\"/tmp/ansible_iNMDpU/ansible_module_file.py\\", line 474, in <module>\\r\\n    main()\\r\\n  File \\"/tmp/ansible_iNMDpU/ansible_module_file.py\\", line 448, in main\\r\\n    open(b_path, \'wb\').close()\\r\\nIOError: [Errno 2] No such file or directory: \'/opt/kevin/task2.txt\'\\r\\n", "msg": "MODULE FAILURE", "rc": 0}
fatal: [172.16.60.233]: FAILED! => {"changed": false, "module_stderr": "", "module_stdout": "Traceback (most recent call last):\\r\\n  File \\"/tmp/ansible_OvTacW/ansible_module_file.py\\", line 474, in <module>\\r\\n    main()\\r\\n  File \\"/tmp/ansible_OvTacW/ansible_module_file.py\\", line 448, in main\\r\\n    open(b_path, \'wb\').close()\\r\\nIOError: [Errno 2] No such file or directory: \'/opt/kevin/task2.txt\'\\r\\n", "msg": "MODULE FAILURE", "rc": 0}

RUNNING HANDLER [task1] **************************************************************************************************************************
        to retry, use: --limit @/etc/ansible/haha.retry

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

如上执行结果,由于/opt/kevin目录不存在,导致task的第二个任务执行失败,这个时候handler根本没有被触发,也就不会执行。
即使第一个任务执行成功,但是它对应的第一个handler也不会被执行!!

###################################################################################
接下来使用--force-handlers选项来强制执行handlers(强制执行的是:成功执行的task对应的handler)
[root@localhost ansible]# ansible-playbook haha.yaml --force-handlers

PLAY [test_host] *********************************************************************************************************************************

TASK [Gathering Facts] ***************************************************************************************************************************
ok: [172.16.60.234]
ok: [172.16.60.233]

TASK [make file task1] ***************************************************************************************************************************
changed: [172.16.60.234]
changed: [172.16.60.233]

TASK [make file task2] ***************************************************************************************************************************
fatal: [172.16.60.233]: FAILED! => {"changed": false, "module_stderr": "", "module_stdout": "Traceback (most recent call last):\\r\\n  File \\"/tmp/ansible_rEJQHm/ansible_module_file.py\\", line 474, in <module>\\r\\n    main()\\r\\n  File \\"/tmp/ansible_rEJQHm/ansible_module_file.py\\", line 448, in main\\r\\n    open(b_path, \'wb\').close()\\r\\nIOError: [Errno 2] No such file or directory: \'/opt/kevin/task2.txt\'\\r\\n", "msg": "MODULE FAILURE", "rc": 0}
fatal: [172.16.60.234]: FAILED! => {"changed": false, "module_stderr": "", "module_stdout": "Traceback (most recent call last):\\r\\n  File \\"/tmp/ansible_7CDxpp/ansible_module_file.py\\", line 474, in <module>\\r\\n    main()\\r\\n  File \\"/tmp/ansible_7CDxpp/ansible_module_file.py\\", line 448, in main\\r\\n    open(b_path, \'wb\').close()\\r\\nIOError: [Errno 2] No such file or directory: \'/opt/kevin/task2.txt\'\\r\\n", "msg": "MODULE FAILURE", "rc": 0}

RUNNING HANDLER [task1] **************************************************************************************************************************
changed: [172.16.60.234]
changed: [172.16.60.233]
        to retry, use: --limit @/etc/ansible/haha.retry

PLAY RECAP ***************************************************************************************************************************************
172.16.60.233              : ok=3    changed=2    unreachable=0    failed=1   
172.16.60.234              : ok=3    changed=2    unreachable=0    failed=1 

如上执行结果,即使playbook执行中有task任务执行失败,但是执行成功的task任务所调用的handler依然会被强制触发并执行!但是执行失败的task任务所调用的handler依然不会被执行。
即handlers中的task1会被执行,task2不会被执行!

2.  tags任务标签
tags用于让用户选择运行playbook中的部分代码。ansible具有幂等性,因此会自动跳过没有变化的部分,即便如此,有些代码为测试其确实没有发生变化的时间依然会非常地长。此时如果确信其没有变化,就可以通过tags跳过此些代码片断。tags可以看作是ansible的任务控制!

ansible的标签(Tags)功能可以给角色(Roles)、文件、单独的任务,甚至整个Playbook打上标签,然后利用这些标签来指定要运行Playbook中的个别任务,或不执行指定的任务。如果有一个很大的playbook剧本,而只想运行playbook其中的某个或部分task任务,而不是运行playbook中所有的任务,这个时候tags是你的最佳选择。

2.1  ansible支持"tags:"属性,执行playbook时,可以通过两种方式根据"tags"过滤任务:
1. 在命令行上,使用或选项"--tags--skip-tags",后面使用空格或"="都可以。
2. 在ansible配置设置中,使用和选项"TAGS_RUNTAGS_SKIP";
3. 可以使用"--list-tags"查看playbook中有哪些tags会被执行;

2.2  ansible系统中内置的特殊tags(目前有5个特殊的tags)
到ansible 2.5版本以后,目前系统内置的tags有以下几个:
always: 除非--skip-tags指定这个标签,否则该标记为always的task一直都会执行。"--tags always"只执行标记了always的tasks;
never: 除非--tags指定了这个标签,否则该标记为never的task一直都不会执行。"--tags never"执行标记了always和never的tasks;
tagged: --tags tagged表示执行所有有tags标签的tasks任务,但不包括tags标签是never的tasks任务;--skip-tags tagged表示所有有tags标签的tasks任务都跳过,即不会执行。
untagged: --tags untagged表示执行所有没有tags标签的tasks任务和tags标签为always的tasks任务;--skip-tags untagged效果相反!
all:--tags all表示执行所有的tags标签为非never的task,包括有tags标签和无tags标签的tasks。

执行ansible-playbook命令时,使用下面两个参数的含义(自定义的tags可以是单个,也可以是多个,多个之间使用逗号隔开):
"--tags 自定义的tag" 表示执行tags为指定的标签名的tasks和tags为always的tasks。如果执行命令ansible-playbook site.yml 时不指定tags,则会执行所有tags为非never的tasks
"--skip-tags 自定义tag" 表示执行所有非指定tag和非never的tasks

2.3  tags标签配置语法有下面三种:

语法一:
tags:
  - tag_test
 
语法二:
tags: tag_test
 
语法三:
tags: [\'tag_test\']

2.3  ansible的tags使用
1)最常见的使用形式。一个task任务添加一个tags标签。

官方示例如下:
[root@localhost ansible]# vim example.yml
---
- hosts: all
  remote_user: root
  gather_facts: no
  tasks:
    - yum: name={{ item }} state=installed
      with_items:
         - httpd
         - memcached
      tags:
         - packages
    - template: src=templates/src.j2 dest=/etc/foo.conf
      tags:
         - configuration

此时如果希望只run其中的某个task,则run的时候指定tags即可。可以运行多个tags,中间使用逗号隔开;也可以运行单个tags。

[root@localhost ansible]# ansible-playbook example.yml --tags "configuration,packages"   
[root@localhost ansible]# ansible-playbook example.yml --tags configuration   
[root@localhost ansible]# ansible-playbook example.yml --tags packages
或者
[root@localhost ansible]# ansible-playbook example.yml --tags="configuration,packages"   
[root@localhost ansible]# ansible-playbook example.yml --tags=configuration   
[root@localhost ansible]# ansible-playbook example.yml --tags=packages

相反,也可以使用--skip-tags跳过某个task任务。

[root@localhost ansible]# ansible-playbook example.yml --skip-tags configuration
或者
[root@localhost ansible]# ansible-playbook example.yml --skip-tags=configuration

具体看下面示例(tags三种语法都用上):

[root@localhost ansible]# cat haha.yaml
---
- hosts: test_host
  remote_user: root
  gather_facts: no
  tasks:
    - name: task1
      file: path=/opt/task1.txt state=touch
      tags: make_task1
    - name: task2
      file: path=/opt/task2.txt state=touch
      tags:
         - make_task2
    - name: task3
      file: path=/opt/task2.txt src=/opt/task2.txt dest=/opt/heihei state=link force=yes
      tags: [\'link_task3\']

只运行make_task1标签的task任务
[root@localhost ansible]# ansible-playbook haha.yaml --tags make_task1

PLAY [test_host] *********************************************************************************************************************************

TASK [task1] *************************************************************************************************************************************
changed: [172.16.60.234]

PLAY RECAP ***************************************************************************************************************************************
172.16.60.234              : ok=1    changed=1    unreachable=0    failed=0   

运行多个tags
[root@localhost ansible]# ansible-playbook haha.yaml --tags make_task1,make_task2

PLAY [test_host] *********************************************************************************************************************************

TASK [task1] *************************************************************************************************************************************
changed: [172.16.60.234]

TASK [task2] *************************************************************************************************************************************
changed: [172.16.60.234]

PLAY RECAP ***************************************************************************************************************************************
172.16.60.234              : ok=2    changed=2    unreachable=0    failed=0

跳过make_task2标签的任务,其他任务正常执行
[root@localhost ansible]# ansible-playbook haha.yaml --skip-tags make_task2

PLAY [test_host] *********************************************************************************************************************************

TASK [task1] *************************************************************************************************************************************
changed: [172.16.60.234]

TASK [task3] *************************************************************************************************************************************
changed: [172.16.60.234]

PLAY RECAP ***************************************************************************************************************************************
172.16.60.234              : ok=2    changed=2    unreachable=0    failed=0 

2)一个task任务添加多个tags标签。
上面是一个task任务添加一个tags标签,其实一个task任务可以添加多个标签,而且不同的task任务可以使用相同的tags标签。

一个任务添加多个tags标签的语法仍然也有三种:
语法1:
tags:
  - tag1
  - tag2

语法2:
tags: tag1,tag2

语法3:
tags: [\'tag1,tag2\']

========================================================
具体示例如下:
[root@localhost ansible]# vim https.yml
---
- hosts: test_host
  remote_user: root
  tasks:
     - name: install httpd package
       tags: 
         - httpd
         - package
       yum:
         name=httpd
         state=latest
 
     - name: start up httpd service
       tags: httpd,service
       service:
         name: httpd
         state: started


上面例子中每个任务都有多个标签,而且上例中两个任务都有一个共同的标签,就是httpd标签。
所以当执行"ansible-playbook httpd.yml --tags=httpd"时,上面两个task任务都会被执行。

由于上面例子中的所有任务都有共同的httpd标签,所以像这种情况,可以把httpd标签提取出来并写在play剧本中,示例如下:
[root@localhost ansible]# vim https.yml
---
- hosts: test_host
  remote_user: root
  tags:httpd
  tasks:
     - name: install httpd package
       tags: 
          - package
       yum:
         name=httpd
         state=latest
 
     - name: start up httpd service
       tags: [\'service\']
       service:
         name: httpd
         state: started

需要注意:当tags写在play剧本中而非写在task任务中时,play中的所有task任务都会继续当前paly中的tags,就像上例中,两个任务都会继承httpds的tag标签,同时还拥有自己的tag标签。

3)内置的特殊tags的用法
上面已经介绍了5个内置的特殊的tags,每个都有其自身的用意。如下以always关键字的tags为例:如果把任务的tags值指定为always时,那么这个任务就总是被执行,除非使用"--skip-tags"选项明确指定不执行对应任务的tags标签。

[root@localhost ansible]# cat haha.yaml 
---
- hosts: test_host
  remote_user: root
  gather_facts: no
  tasks:
    - name: task1
      file: path=/opt/task1.txt state=touch
      tags: make_task1
    - name: task2
      file: path=/opt/task2.txt state=touch
      tags:
         - always
    - name: task3
      file: path=/opt/task2.txt src=/opt/task2.txt dest=/opt/heihei state=link force=yes
      tags: [\'link_task3\']

执行1:如下,虽然tags指定了执行标签为make_task1的任务,但是由于任务2的标签有关键字always,所以任务2也会被执行,这就是always的作用!
[root@localhost ansible]# ansible-playbook haha.yaml --tags=make_task1

PLAY [test_host] *********************************************************************************************************************************

TASK [task1] *************************************************************************************************************************************
changed: [172.16.60.234]

TASK [task2] *************************************************************************************************************************************
changed: [172.16.60.234]

PLAY RECAP ***************************************************************************************************************************************
172.16.60.234              : ok=2    changed=2    unreachable=0    failed=0  

执行2: 只执行标签为always的任务
[root@localhost ansible]# ansible-playbook haha.yaml --tags always
或者
[root@localhost ansible]# ansible-playbook haha.yaml --tags=always

PLAY [test_host] *********************************************************************************************************************************

TASK [task2] *************************************************************************************************************************************
changed: [172.16.60.234]

PLAY RECAP ***************************************************************************************************************************************
172.16.60.234              : ok=1    changed=1    unreachable=0    failed=0

执行3: 跳过标签为always关键字的任务,这里明确指出跳过执行always标签。
[root@localhost ansible]# ansible-playbook haha.yaml --skip-tags always    

PLAY [test_host] *********************************************************************************************************************************

TASK [task1] *************************************************************************************************************************************
changed: [172.16.60.234]

TASK [task3] *************************************************************************************************************************************
ok: [172.16.60.234]

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

其他四个特殊的tags标签在这里就不做示例说明了。特殊tags标签可以在ansible-playbook命令执行时直接使用。

4)tags标签可以和role 结合使用

[root@localhost ansible]# cat test.yml
---
roles:
  - { role: webserver, port: 5000, tags: [ \'web\', \'foo\' ] }

5)tags和include结合使用。

[root@localhost ansible]# cat test.yml
---
- include: kevin.yml tags=web,foo

如上,对一个include任务打了两个tags标签,直接执行"ansible_playbook test.yml" 或 "ansible_playbook test.yml --tags=web" 或 "ansible_playbook test.yml --tags=foo" 命令则会将kevin.yml文件中所有task任务都执行。

再来看看一个include结合tags的示例:通过指定标签(tags),来说明是安装tomcat7还是tomcat8

tomcat.yml文件

---
- include: install_tomcat7.yml
  tags: tomcat7
- include: install_tomcat8.yml
  tags: tomcat8

install_tomcat7.yml文件

---
- name: "复制文件到远程主机"
  copy:
    src={{ item.src }}
    dest={{ item.dest }}
  with_items:
    - src: jdk-7u79-linux-x64.rpm
      dest: /usr/local/src/
    - src: java17.sh
      dest: /etc/profile.d/
- name: "安装jdk"
  yum:
    name: /usr/local/src/jdk-7u79-linux-x64.rpm
    state: present
- name: "重新加载环境变量"
  shell: "source /etc/profile.d/java17.sh"
- name: "复制tomcat文件到远程服务器并解压"
  unarchive:
    src=apache-tomcat-7.0.64.zip
    dest=/data/
    copy=yes
    owner=staplesapp
    group=admin
- name: "对解压后的文件重命名"
  shell: mv /data/apache-tomcat-7.0.64 /data/tomcat7
- name: "对tomcat进行相关配置"
  shell: find /data/tomcat7/bin -name "*.sh" | xargs chmod +x
- name: "启动tomcat"
  shell: \'nohup /data/tomcat7/bin/startup.sh &\'

install_tomcat8.yml文件

---
- name: "复制文件到远程主机"
  copy:
    src={{ item.src }}
    dest={{ item.dest }}
  with_items:
    - src: jdk-8u111-linux-x64.rpm
      dest: /usr/local/src/
    - src: java18.sh
      dest: /etc/profile.d/
- name: "安装jdk"
  yum:
    name: /usr/local/src/jdk-8u111-linux-x64.rpm
    state: present
- name: "配置java环境变量"
  shell: "source /etc/profile.d/java18.sh"
- name: "安装tomcat"
  unarchive:
      src=apache-tomcat-8.0.30.tar.gz
      dest=/data/
      copy=yes
      owner=staplesapp
      group=admin
- name: "对解压后的文件重命名"
  shell: mv /data/apache-tomcat-8.0.30 /data/tomcat8
- name: "启动tomcat"
  shell: \'nohup /data/tomcat8/bin/startup.sh &\'

下面开始执行命令:

安装tomcat7:
[root@localhost ansible]# ansible-playbook tomcat.yml --tags tomcat7

安装tomcat8:
[root@localhost ansible]# ansible-playbook tomcat.yml --tags tomcat8

这里需要特别注意:
在之前ansible版本中使用include 整合多个roles至统一入口结合tags标签来管理roles剧本,但在ansible2.8版本之后将会删除in

以上是关于php-cgi进程过多,导致系统资源耗尽的主要内容,如果未能解决你的问题,请参考以下文章

服务器php-cgi.exe进程过多,导致CPU占用100%

php-fpm进程占用CPU过高| php-cgi进程过多

避免PHP-FPM内存泄漏导致内存耗尽

求php-cgi.exe使用率过高导致CPU100%的解决办法

【转】ksoftirqd进程耗尽单核100%si处理软中断导致性能瓶颈

服务器大量php-cgi.exe进程,导致CPU占用100%的解决