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