Ansible's Playbook mission control

1. Basic introduction to Ansible task control

Here we mainly introduce the mission control in PlayBook.
Mission control is similar to logical control statements such as if ... , for ..., etc. in programming languages.
Here we give a practical application case to illustrate how mission control is applied in PlayBook.
In the PlayBook below, we created three users: tomcat, www and mysql. Install the Nginx software package, update the Nginx main configuration file and the virtual host configuration file at the same time, and finally make the Nginx service start.
The entire PlayBook has no grammatical problems, but there are still some areas that need our attention and optimization in terms of logic and writing:

  • Nginx startup logic lacks consideration. If the syntax of the Nginx configuration file is wrong, it will cause the failure to start Nginx, so that the execution of the PlayBook will fail.
  • To create users in batches, the list of instructions is too rigid. If you create several more users, it will be difficult to end.

code show as below:

---
- name: task control playbook example
 hosts: webservers
 tasks:
 - name: create tomcat user
   user: name=tomcat state=present
 
 - name: create www user
   user: name=www state=present
 
 - name: create mysql user
   user: name=mysql state=present
 
 - name: yum nginx webserver
   yum: name=nginx state=present
 
 - name: update nginx main config
   copy: src=nginx.conf dest=/etc/nginx/
 
 - name: add virtualhost config
   copy: src=www.qfedu.com.conf dest=/etc/nginx/conf.d/
 
 - name: start nginx server
   service: name=nginx state=started

Preparation:

cat nginx.conf

user www;
worker_processes 2;
error_log /var/log/nginx/error.log;
pid /var/run/nginx.pid;
events {
    
    
 worker_connections 1024;
}
http {
    
    
 include /etc/nginx/mime.types;
 default_type application/octet-stream;
log_format main '$remote_addr - $remote_user
[$time_local] "$request" '
 '$status $body_bytes_sent
"$http_referer" '
 '"$http_user_agent"
"$http_x_forwarded_for"';
 sendfile on;
 tcp_nopush on;
 keepalive_timeout 0;
 gzip on;
 gzip_min_length 1k;
 gzip_buffers 8 64k;
 gzip_http_version 1.0;
 gzip_comp_level 5;
 gzip_types text/plain application/x-javascript
text/css application/json application/xml
application/x-shockwave-flash application/javascript
image/svg+xml image/x-icon;
 gzip_vary on;
 include /etc/nginx/conf.d/*.conf;
}
cat www.qfedu.com.conf

server {
    
    
 listen 80;
 server_name www.qfedu.com;
 root /usr/share/nginx/html;
 access_log /var/log/nginx/www.qfedu.comaccess_log main;
error_log /var/log/nginx/www.qfedu.comerror_log;
 add_header Access-Control-Allow-Origin *;
 location ~ .*\.(gif|jpg|jpeg|png|bmp|swf)$ {
    
    
 expires 1d;
 }
 location ~ .*\.(js|css)?$ {
    
    
 expires 1d;
 }
}

2. Condition judgment

solve the first problem

Nginx startup logic lacks consideration. If the syntax of the Nginx configuration file is wrong, it will cause the failure to start Nginx, so that the execution of the PlayBook will fail.
If we can verify the correctness of the Nginx configuration file syntax before starting, we will start or restart Nginx only when the verification is passed; otherwise, skip the process of starting Nginx. This will avoid the risk that Nginx cannot be started due to syntax problems in the Nginx configuration file.

Nginx syntax check
- name: check nginx syntax
 shell: /usr/sbin/nginx -t

So how to associate the task of Nginx grammar check with the task started by Nginx?
It would be a good idea if we can get the result of the task of grammar check and judge whether the "starting task of NGINX" is executed according to the result. plan. How to get the results of syntax checking task? At this point, you can use the registration variables in Ansible that you learned earlier.

Get Task task results
- name: check nginx syntax
 shell: /usr/sbin/nginx -t
 register: nginxsyntax
Use the debug module to confirm the data structure of the returned result

At this time, there may be doubts. I got the task result, but what is the content in the result, and how can I use it in the subsequent PlayBook according to the content?

- name: print nginx syntax result
 debug: var=nginxsyntax

Through the debug module, print out the returned results. When nginxsyntax.rc is 0, the syntax check is correct.

"nginxsyntax": {
    
    
 "changed": true,
 "cmd": "/usr/sbin/nginx -t",
 "delta": "0:00:00.012045",
 "end": "2017-08-12 20:19:04.650718",
 "rc": 0,
 "start": "2017-08-12 20:19:04.638673",
 "stderr": "nginx: the configuration file
/etc/nginx/nginx.conf syntax is ok\nnginx:
configuration file /etc/nginx/nginx.conf test is
successful",
"stderr_lines": [
 "nginx: the configuration file
/etc/nginx/nginx.conf syntax is ok",
 "nginx: configuration file
/etc/nginx/nginx.conf test is successful"
 ],
 "stdout": "",
 "stdout_lines": []
 }
Use the conditional judgment (when) instruction to use the result of the syntax check
- name: check nginx syntax
 shell: /usr/sbin/nginx -t
 register: nginxsyntax
 - name: print nginx syntax
 debug: var=nginxsyntax
 
 - name: start nginx server
 service: name=nginx state=started
 when: nginxsyntax.rc == 0
Improved PlayBook
---
- name: task control playbook example
 hosts: webservers
 gather_facts: no
 tasks:
  - name: create tomcat user
    user: name=tomcat state=present
  
  - name: create www user
    user: name=www state=present
  
  - name: create mysql user
    user: name=mysql state=present
 
  - name: yum nginx webserver
    yum: name=nginx state=present
  
  - name: update nginx main config
    copy: src=nginx.conf dest=/etc/nginx/
  
  - name: add virtualhost config
    copy: src=www.qfedu.com.conf dest=/etc/nginx/conf.d/
 
  - name: check nginx syntax
    shell: /usr/sbin/nginx -t
    register: nginxsyntax
 
  - name: print nginx syntax
    debug: var=nginxsyntax
 
  - name: start nginx server
    service: name=nginx state=started
    when: nginxsyntax.rc == 0
Summarize

With the above logic, as long as the syntax check passes, the task "start nginx server" will be executed. In solving this problem, we learned the combination of when conditional judgment and registration variables. Learned when conditional judgment can support complex logic. For example, the logical operator and is used now.
In addition, when supports the following operators:

==
!=
> >=
< <=
is defined
is not defined
true
false
#⽀持逻辑运算符: and or

3. Loop control

solve the second problem

To create users in batches, the list of instructions is too rigid. If you create several more users, it will be difficult to end.
If you ignore the implementation of PlayBook when creating users, simply use the shell to create some users in batches. How do you usually write it?
For example, the following script:

#! /bin/bash

createuser="tomcat mysql www"
for i in `echo $createuser`
do
 useradd $i
done

Then if there is such a loop control in PlayBook, we can also complete the multi-user creation work as simply as writing a shell.
Use with_items in PlayBook to implement loop control, and the intermediate variable during the loop (the $i variable in the above shell loop) can only be the keyword item, and cannot be customized at will.

Based on the above, the improved PlayBook

Here, the script variable createuser (a list) is defined, and then the variable is looped through with_items to achieve the purpose of creating users.

- name: variable playbook example
 hosts: webservers
 gather_facts: no
 vars:
   createuser:
    - tomcat
    - www
    - mysql
 tasks:
   - name: create user
     user: name={
    
    {
    
     item }} state=present
     with_items: "{
    
    { createuser }}"
   
   - name: yum nginx webserver
     yum: name=nginx state=present
   
   - name: update nginx main config
     copy: src=nginx.conf dest=/etc/nginx/
 
   - name: add virtualhost config
     copy: src=www.qfedu.com.conf dest=/etc/nginx/conf.d/
   
   - name: check nginx syntax
     shell: /usr/sbin/nginx -t
     register: nginxsyntax
 
   - name: print nginx syntax
     debug: var=nginxsyntax

   - name: start nginx server
     service: name=nginx state=started
     when: nginxsyntax.rc == 0

After solving the above problems, the entire PlayBook has been greatly improved.

new version cycle
- name: loop item
  hosts: all
  gather_facts: no
  vars:
    some_list:
     - "a"
     - "b"
     - "c"
    num_list:
     - 1
     - 2
     - 3
     - 5
 tasks:
 - name: show item
   debug:
     var: "{
    
    { item }}"
   loop: "{
    
    { some_list }}"
 
- name: show item when item > 3
  debug:
   var: "{
    
    { item }}"
  loop: "{
    
    { num_list }}"
  when: item > 3

Consider such a situation:
After updating the Nginx configuration file, we need to publish the new configuration to the production server through PlayBook, and then reload our Nginx service. But taking the current PlayBook as an example, each time the Nginx configuration file is changed, it can be released to production through it, but the entire PlayBook needs to be executed once, which virtually expands the scope of changes and the risk of changes.
The following Tags attribute can solve this problem.

Four, Tags property

We can use the tags attribute in Play to solve the problems of expanding the scope of changes and changing risks caused by the current PlayBook changes.
In the improved PlayBook, for the file publishing TASK tasks
"update nginx main config" and "add virtualhost config",
the attribute tags are added, and the attribute value is updateconfig.
In addition, we added the "reload nginx server" TASK task. After the configuration file is updated, reload the Nginx service.
That reloading depends on the Nginx service being started. Therefore, it is necessary to further judge the existence of the Nngix pid file to prove that the Nginx service itself is starting, and the Nginx service can be reloaded only during startup.

To determine whether a file exists use the stat module
- name: check nginx running
  stat: path=/var/run/nginx.pid
  register: nginxrunning

Observing the results, you will find that the value of nginxrunning.stat.exists is true to indicate the startup status, and false to indicate the shutdown status.
Next, you can decide whether to reload the Nginx service based on this result.

Improve PlayBook
- name: tags playbook example
  hosts: webservers
  gather_facts: no
  vars:
   createuser:
     - tomcat
     - www
     - mysql
  tasks:
     - name: create user
       user: name={
    
    {
    
     item }} state=present
       with_items: "{
    
    { createuser }}"
 
     - name: yum nginx webserver
       yum: name=nginx state=present
 
     - name: update nginx main config
       copy: src=nginx.conf dest=/etc/nginx/
       tags: updateconfig
 
     - name: add virtualhost config
       copy: src=www.qfedu.com.conf dest=/etc/nginx/conf.d/
       tags: updateconfig
 
     - name: check nginx syntax
       shell: /usr/sbin/nginx -t
       register: nginxsyntax
       tags: updateconfig
 
     - name: check nginx running
       stat: path=/var/run/nginx.pid
       register: nginxrunning
       tags: updateconfig
 
     - name: print nginx syntax
       debug: var=nginxsyntax
 
     - name: print nginx syntax
       debug: var=nginxrunning
 
     - name: reload nginx server
       service: name=nginx state=started
       when: nginxsyntax.rc == 0 and
             nginxrunning.stat.exists == true
      tags: updateconfig
 
    - name: start nginx server
      service: name=nginx state=started
      when:
        - nginxsyntax.rc == 0
        - nginxrunning.stat.exists == false
      tags: updateconfig
Specify tags to execute PlayBook

Tags must be specified during execution, so that only tasks tagged with updateconfig on the task task will be executed during execution

ansible-playbook -i hosts site.yml -t updateconfig

Five **, Handlers attribute **

Observing the current Playbook, I can't find that when my configuration file has not changed, the TASK "reload nginx server" will still be triggered every time.
How to trigger TASK "reload nginx server" only when the configuration file changes is the most perfect implementation. This is where the handlers attribute can be used.

Improve PlayBook
 - name: handlers playbook example
   hosts: webservers
   gather_facts: no
   vars:
     createuser:
        - tomcat
        - www
        - mysql
   tasks:
     - name: create user
       user: name={
    
    {
    
     item }} state=present
       with_items: "{
    
    { createuser }}"
    
     - name: yum nginx webserver
       yum: name=nginx state=present
 
     - name: update nginx main config
       copy: src=nginx.conf dest=/etc/nginx/
       tags: updateconfig
       notify: reload nginx server
 
    - name: add virtualhost config
      copy: src=www.qfedu.com.conf dest=/etc/nginx/conf.d/
      tags: updateconfig
      notify: reload nginx server
 
   - name: check nginx syntax
     shell: /usr/sbin/nginx -t
     register: nginxsyntax
     tags: updateconfig
  
   - name: check nginx running
     stat: path=/var/run/nginx.pid
     register: nginxrunning
     tags: updateconfig
 
   - name: start nginx server
     service: name=nginx state=started
     when:
      - nginxsyntax.rc == 0
      - nginxrunning.stat.exists == false
 handlers:
   - name: reload nginx server
    service: name=nginx state=reloaded
    when:
      - nginxsyntax.rc == 0
      - nginxrunning.stat.exists == true

In the improved PlayBook, we have added a new attribute notify to the file publishing TASK tasks "update nginx main config" and "add virtualhost config", with a value of "reload nginx server".
It means to set up a notification mechanism for the TASK issued by these two files. When Ansible thinks that the content of the file has changed (the MD5 of the file has changed), it will send a notification Signal to notify a certain task in handlers. Which task to send to the handlers is determined by the value of notify "reload nginx server". After the notification is sent, the handlers will look for the task named "reload nginx server" among the related tasks in the handlers according to the sent notification. When a task with such a name is found, it will be executed. If not found, do nothing. If we want to implement such a mechanism, we must pay attention to the value set by the notify attribute, and we must ensure that it corresponds to the TASK name in handlers.

carry out

Execute for the first time, if the configuration file has not changed, you can find that the TASK task in the handlers is not triggered at all

ansible-playbook -i hosts site.yml -t updateconfig

Manually modify the Nginx configuration file slightly, as long as the MD5 check value changes. Execute again at this time, and found that the TASK task in handlers was triggered

ansible-playbook -i hosts site.yml -t updateconfig

Guess you like

Origin blog.csdn.net/xiaoyu070321/article/details/131366846