codecamp

Ansible 在 Playbooks 使用 Handlers

Handlers 是我们在 Ansible Playbooks 里很常用来重开系统服务 (Service) 的手法,大家可以在不少前人分享的 Playbooks 里看见它的踪迹,这里冻仁将透过安装 Nginx 的 Playbook 来介绍它。

automate_with_ansible_practice-20.jpg

Handlers 是什么?

Handler 本身是一种非同步的 callback function 1;在这里则是指关连于特定 tasks 的事件 (event) 触发机制。当这些特定的 tasks 状态为被改变 (changed) 且都已被执行时,才会触发一次 event。

以上图为例,要触发 restart nginx 这个 handler,需符合以下条件:

  1. modify index.html 或 turn server_tokens off 两个 tasks 中,至少有一个状态为 changed
  2. 所有关连到 restart nginx handler 的 tasks 都已被执行。 2

怎么使用 Handlers?

底下冻仁将通过部署 Nginx 的 Playbook 为例。

  1. 建立 ansible.cfg。

    $ vi ansible.cfg
    [defaults]
    
    hostfile = inventory
    remote_user = docker
    private_key_file = ~/.ssh/id_rsa
    host_key_checking = False
    retry_files_save_path = ./ansible-retry
    
  2. 建立 inventory file。

    $ vi inventory
    server1  ansible_ssh_host=192.168.1.104  ansible_ssh_port=2221
    
  3. 建立 setup_nginx.yml。

    $ vi setup_nginx.yml
    ---
    
    - name: setup the nginx
     hosts: all
     become: true
     vars:
       username: "ironman"
       mail: "chusiang (at) drx.tw"
       blog: "http://note.drx.tw"
    
     tasks:
       # 执行 'apt-get update' 指令。
       - name: update apt repo cache
         apt: name=nginx update_cache=yes
    
       # 执行 'apt-get install nginx' 指令。
       - name: install nginx with apt
         apt: name=nginx state=present
    
       # 于网页根目录 (DocumentRoot) 编辑 index.html。
       - name: modify index.html
         template: >
           src=templates/index.html.j2
           dest=/usr/share/nginx/html/index.html
           owner=www-data
           group=www-data
           mode="644"
           backup=yes
         notify: restart nginx
    
       # (security) 关闭 server_tokens:移除 server_tokens 前的 '#' 字元。
       - name: turn server_tokens off
         lineinfile: >
           dest=/etc/nginx/nginx.conf
           regexp="server_tokens off;"
           insertafter="# server_tokens off;"
           line="server_tokens off;"
           state=present
         notify: restart nginx
    
     # handlers 
     #
     # * 当确认事件有被触发才会动作。
     # * 一个 handler 可被多个 task 通知 (notify),并于 tasks 跑完才会执行。
     handlers:
       # 执行 'sudo service nginx restart' 指令。
       - name: restart nginx
         service: name=nginx enabled=yes state=restarted
    
     # post_tasks:
     #
     # 在 tasks 之后执行的 tasks。
     post_tasks:
       # 检查网页内容。
       - name: review http state
         command: "curl -s http://localhost"
         register: web_context
    
       # 印出检查结果。
       - name: print http state
         debug: msg=
    
    # vim:ft=ansible :
    
    1. 在第 47 行里,我们建立了一个 restart nginx handler。
    2. 在修改到 Nginx 设定档的 tasks (modify index.htmlturn server_tokens off) 里,使用 notify 通知 handlers (restart nginx) 说这些 Tasks 要进行关连。
    3. 最后在 post_tasks 里建了 2 个 tasks,让它们可以在一般的 tasks 结束后才执行。
  4. 建立 Nginx vhost 的 template:请参考前一篇的Ansible 使用 Template 系统,冻仁就不在此多加详述。

    $ mkdir templates && vi templates/index.html.j2
    <!DOCTYPE html>
    <html>
     <head>
       <meta charset="UTF-8">
       <title>Day15 demo | automate-with-ansible</title>
     </head>
     <style type="text/css" media="all">
       body {
         font-size: x-large;
       }
     </style>
     <body>
       <p>
    <pre>[ @automate-with-ansible ~ ]$ hostname
    automate-with-ansible.drx.tw
    [ @automate-with-ansible ~ ]$ cowsay "This is a ansible-playbook demo for automate-with-ansible at 2016/12/15."
    _____________________________________
    / This is a ansible-playbook demo for \
    \ automate-with-ansible at 2016/12/15./
    -------------------------------------
           \   ^__^
            \  (oo)\_______
               (__)\       )\/\
                   ||----w |
                   ||     ||
    [ @automate-with-ansible ~ ]$
    [ @automate-with-ansible ~ ]$
    [ @automate-with-ansible ~ ]$ cat .profile
    - 
    - </pre>
       </p>
     </body>
    </html>
    
    • 在这份 index.html.j2 里,我们用了 usernamemail 和 blog 三个变数,其值会从 setup_nginx.yml 中代入。
  5. 执行 Playbook。

    2016-12-15-ansible-handlers.gif

    • 试着多跑几次,就会发现当 modify index.html 和 turn server_tokens off tasks 的状态不为 changed 时,该 handler 不会被触发的差异。
    • 在此例中,我们可以借由修改 Playbook 里的变数 (vars) 来重复触发 handler,例如把 username从 ironman 修改成 root

后话

虽然我们可以在 tasks 的最后加个 task 来重开 web service,可当与 web service 相关的 tasks 的状态皆为 ok 时,这种写法会让 web service 再次被重开。

通过 Handlers 我们可以只在需要时重开一次,进而减少服务中断的时间

相关连结

1. 维基百科对于 Handler 的解释为 An asynchronous callback (computer programming) subroutine in computing,详情请参考 Handler | Wikipedia 一文。 ↩
2. 一般都会用 Tasks 通知 (notify) Handlers 来述叙两者的关系,但冻仁比较喜欢用 Tasks 关连于 Handlers 的说法。 ↩


Ansible 使用 Template 系统
Ansible 发送 notification 到 Slack
温馨提示
下载编程狮App,免费阅读超1000+编程语言教程
取消
确定
目录

关闭

MIP.setData({ 'pageTheme' : getCookie('pageTheme') || {'day':true, 'night':false}, 'pageFontSize' : getCookie('pageFontSize') || 20 }); MIP.watch('pageTheme', function(newValue){ setCookie('pageTheme', JSON.stringify(newValue)) }); MIP.watch('pageFontSize', function(newValue){ setCookie('pageFontSize', newValue) }); function setCookie(name, value){ var days = 1; var exp = new Date(); exp.setTime(exp.getTime() + days*24*60*60*1000); document.cookie = name + '=' + value + ';expires=' + exp.toUTCString(); } function getCookie(name){ var reg = new RegExp('(^| )' + name + '=([^;]*)(;|$)'); return document.cookie.match(reg) ? JSON.parse(document.cookie.match(reg)[2]) : null; }