09_playbook application and roles automation batch installation example, simple yaml example, task list, characteristics, details of task definition, notify and handler, tag, include and roles

12. Playbook application and roles automation batch installation example
12.1. Simple example of yaml
12.2. Simple example of ansible-playbook command description and playbook writing:
12.3. Content of
playbook 12.4. task list
12.4.1. Features
12.4.2. Details of defining tasks
12.4.3.notify and handler
12.4.4.tag tag
12.5.include and roles
12.5.1.include
12.5.2.roles
12.6.roles Example: batch automation installation

12. Example of automatic batch installation of playbook application and roles

Reposted from: https://www.cnblogs.com/f-ck-need-u/p/7567417.html

Playbook is the most important means for ansible to realize batch automation. Functions such as variables, references, and loops can be used in it. Compared with ad-hoc, its functions are much more powerful.

12.1.yaml simple example

Ansible's playbook uses yaml syntax. The following is a file in yaml format:

---
# Members in Bob’s family
name: Bob
age: 30
gender: Male
wife:
   name: Alice
   age: 27
   gender: Female
children:
   - name: Jim
age: 6
gender: Female
   - name: Lucy
age: 3
gender: Female

12.2. Ansible-playbook command description and simple example of playbook writing:

The following is a simple playbook example. This example performs two tasks. The first task is to execute a /bin/date command, and the second task is to copy the /etc/profile file to /tmp on the target host. They use the ansible command module and copy respectively module.

[root@node2 tmp]# cat test.yaml
---
- hosts: aaa
  tasks:
    - name: execute date cmd
      command: /bin/date
    - name: copy profile to /tmp
      copy: src=/etc/profile dest=/tmp
[root@node2 tmp]#

Note that the host configuration for ansible is:

[root@node2 ansible]# cat /etc/ansible/hosts

[abc]              # 自定义一个组名
172.17.0.3         # 添加被管理主机的IP

[aaa]
172.17.0.4

[bbb]
172.17.0.5
172.17.0.6

[webserver]
172.17.0[3:6]

After writing the playbook, use the ansible-playbook command to execute it. The ansible-playbook command options are mostly the same as the ansible command options. But it also has its own options. The following is the intercepted help information:

[root@node2 tmp]# ansible-playbook --help
usage: ansible-playbook [-h] [--version] [-v] [-k]
                        [--private-key PRIVATE_KEY_FILE] [-u REMOTE_USER]
                        [-c CONNECTION] [-T TIMEOUT]
                        [--ssh-common-args SSH_COMMON_ARGS]
                        [--sftp-extra-args SFTP_EXTRA_ARGS]
                        [--scp-extra-args SCP_EXTRA_ARGS]
                        [--ssh-extra-args SSH_EXTRA_ARGS] [--force-handlers]
                        [--flush-cache] [-b] [--become-method BECOME_METHOD]
                        [--become-user BECOME_USER] [-K] [-t TAGS]
                        [--skip-tags SKIP_TAGS] [-C] [--syntax-check] [-D]
                        [-i INVENTORY] [--list-hosts] [-l SUBSET]
                        [-e EXTRA_VARS] [--vault-id VAULT_IDS]
                        [--ask-vault-pass | --vault-password-file VAULT_PASSWORD_FILES]
                        [-f FORKS] [-M MODULE_PATH] [--list-tasks]
                        [--list-tags] [--step] [--start-at-task START_AT_TASK]
                        playbook [playbook ...]

Runs Ansible playbooks, executing the defined tasks on the targeted hosts.

positional arguments:
  playbook              Playbook(s)

optional arguments:
  --ask-vault-pass      ask for vault password
  --flush-cache         clear the fact cache for every host in inventory
  --force-handlers      run handlers even if a task fails
  --list-hosts          outputs a list of matching hosts; does not execute
                        anything else
  --list-tags           list all available tags
  --list-tasks          list all tasks that would be executed
  --skip-tags SKIP_TAGS
                        only run plays and tasks whose tags do not match these
                        values
  --start-at-task START_AT_TASK
                        start the playbook at the task matching this name
  --step                one-step-at-a-time: confirm each task before running
  --syntax-check        perform a syntax check on the playbook, but do not
                        execute it
  --vault-id VAULT_IDS  the vault identity to use
  --vault-password-file VAULT_PASSWORD_FILES
                        vault password file
  --version             show program's version number, config file location,
                        configured module search path, module location,
                        executable location and exit
  -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
  -M MODULE_PATH, --module-path MODULE_PATH
                        prepend colon-separated path(s) to module library (def
                        ault=~/.ansible/plugins/modules:/usr/share/ansible/plu
                        gins/modules)
  -e EXTRA_VARS, --extra-vars EXTRA_VARS
                        set additional variables as key=value or YAML/JSON, if
                        filename prepend with @
  -f FORKS, --forks FORKS
                        specify number of parallel processes to use
                        (default=5)
  -h, --help            show this help message and exit
  -i INVENTORY, --inventory INVENTORY, --inventory-file INVENTORY
                        specify inventory host path or comma separated host
                        list. --inventory-file is deprecated
  -l SUBSET, --limit SUBSET
                        further limit selected hosts to an additional pattern
  -t TAGS, --tags TAGS  only run plays and tasks tagged with these values
  -v, --verbose         verbose mode (-vvv for more, -vvvv to enable
                        connection debugging)

Connection Options:
  control as whom and how to connect to hosts

  --private-key PRIVATE_KEY_FILE, --key-file PRIVATE_KEY_FILE
                        use this file to authenticate the connection
  --scp-extra-args SCP_EXTRA_ARGS
                        specify extra arguments to pass to scp only (e.g. -l)
  --sftp-extra-args SFTP_EXTRA_ARGS
                        specify extra arguments to pass to sftp only (e.g. -f,
                        -l)
  --ssh-common-args SSH_COMMON_ARGS
                        specify common arguments to pass to sftp/scp/ssh (e.g.
                        ProxyCommand)
  --ssh-extra-args SSH_EXTRA_ARGS
                        specify extra arguments to pass to ssh only (e.g. -R)
  -T TIMEOUT, --timeout TIMEOUT
                        override the connection timeout in seconds
                        (default=10)
  -c CONNECTION, --connection CONNECTION
                        connection type to use (default=smart)
  -k, --ask-pass        ask for connection password
  -u REMOTE_USER, --user REMOTE_USER
                        connect as this user (default=None)

Privilege Escalation Options:
  control how and which user you become as on target hosts

  --become-method BECOME_METHOD
                        privilege escalation method to use (default=sudo), use
                        `ansible-doc -t become -l` to list valid choices.
  --become-user BECOME_USER
                        run operations as this user (default=root)
  -K, --ask-become-pass
                        ask for privilege escalation password
  -b, --become          run operations with become (does not imply password
                        prompting)
[root@node2 tmp]# 

Now execute the above test.yaml

[root@node2 tmp]# ansible-playbook /tmp/test.yaml

PLAY [aaa] *************************************************************************************************************************************

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

TASK [execute date cmd] ************************************************************************************************************************
changed: [172.17.0.4]

TASK [copy profile to /tmp] ********************************************************************************************************************
changed: [172.17.0.4]

PLAY RECAP *************************************************************************************************************************************
172.17.0.4                 : ok=3    changed=2    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

[root@node2 tmp]# 

From the above results, it can be seen that:

  • (1) By default, ansible-playbook is the same as ansible, both are in synchronous blocking mode, and a task needs to be executed on all hosts before continuing to the next task;
  • (2) Fact information will be collected automatically before execution;
  • (3) From the displayed results, it can be judged whether the task was actually executed, or whether it was not executed because of idempotence.
  • (4) Each play contains several tasks, and has response information play recap.

12.3. Contents of playbook

For each play in the playbook, use the hosts option to define the host or host group to perform these tasks. You can also use remote_user to specify the user who performs the task on the remote host. In fact, remote_user is the user who ssh connects to the controlled host , the identity that executes the command will naturally be this user.

For example:

---
- hosts: abc,aaa,172.17.0.5
 remote_user: root
 tasks: XXXX

Although "," can be used to separate hosts or host groups at hosts, this method is not introduced in the official manual. In addition, there are several ways to specify hosts and host groups:

  • all or *: indicates all hosts in the inventory.
  • :: Take union. For example, "host1:host2:group1" means 2 hosts plus a host group.
  • :&: Take the intersection. For example "group1:&group2" means hosts that are in both hostgroups.
  • :!:exclude. For example, "group1:!host1" means the remaining hosts in group1 excluding the host1 host.
  • Wildcard: for example "web*.baidu.com".
  • Number range: for example "web[0-5].baidu.com".
  • Letter range: for example "web[ad].baidu.com".
  • Regular expression: start with " ". For example " web\d.baidu.com".

In addition, on the ansible command line or ansible-playbook command line, you can use the "-l" option to limit the hosts that execute the task. For example:

ansile centos -l host[1:5] -m ping

Indicates that only host1 to host5 in the centos host group executes the ping module.

It is also possible to define the identity of executing the task separately on a task, which will override the global definition.

[root@node2 tmp]# cat test2.yaml
---
- hosts: abc,aaa,bbb
  remote_user: root
  tasks:
    - name: run a command
      shell: /bin/date
    - name: copy a file to /tmp
      copy: src=/etc/profile dest=/tmp
      remote_user: myuser
[root@node2 tmp]#

运行过程是:
[root@node2 tmp]# ansible-playbook test2.yaml

PLAY [abc,aaa,bbb] *****************************************************************************************************************************

TASK [Gathering Facts] *************************************************************************************************************************
ok: [172.17.0.6]
ok: [172.17.0.5]
ok: [172.17.0.4]
ok: [172.17.0.3]

TASK [run a command] ***************************************************************************************************************************
changed: [172.17.0.5]
changed: [172.17.0.6]
changed: [172.17.0.3]
changed: [172.17.0.4]

TASK [copy a file to /tmp] *********************************************************************************************************************
changed: [172.17.0.4]
changed: [172.17.0.5]
changed: [172.17.0.3]
changed: [172.17.0.6]

PLAY RECAP *************************************************************************************************************************************
172.17.0.3                 : ok=3    changed=2    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
172.17.0.4                 : ok=3    changed=2    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
172.17.0.5                 : ok=3    changed=2    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
172.17.0.6                 : ok=3    changed=2    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

[root@node2 tmp]#

You can go to the /tmp directory on the 172.17.0.[3-6] machine to check if there is a profile

Also supports privilege escalation:

[root@node2 tmp]# cat test2.yaml
---
- hosts: abc,aaa,bbb
  remote_user: yourname
  tasks:
    - name: run a command
      shell: /bin/date
    - name: copy a file to /tmp
      copy: src=/etc/profile dest=/tmp
      become: yes
      become_method: sudo
      become_user: root

From the above example, it can be seen that remote_user is not actually the absolute identity for executing the task, it is just the identity of the ssh connection, but when no become is specified, it happens to use this identity to run the task.

12.4.task list

12.4.1. Features

Each play contains a hosts and a tasks, hosts defines the hosts to be controlled in the inventory, tasks defines a series of task task list, such as calling each module. These tasks are executed one at a time in sequence, and will not move to the next task until all the selected hosts have executed the task.

It should be noted that although only the filtered hosts will execute the corresponding task, all hosts (all hosts here means those specified by the hosts option) will receive the same task command, and all hosts receive After receiving the command, the ansible master will screen certain hosts and perform tasks remotely through ssh. That is to say, if you check the information of ansible-playbook -vvvv, you will find that the temporary task file will be sent to all the controlled hosts through sftp, but only some of the hosts that are screened (if screened) will ssh past and Execute commands remotely.

When a controlled host makes an error or fails to execute a certain task, it will be removed from the task polling list. That is to say, for a certain host, if a certain task fails to execute, all subsequent tasks will not be executed again. Of course, this will not affect other hosts to perform tasks (unless there are dependencies between tasks on the host).

Most importantly, tasks in ansible are idempotent, and multiple executions will not affect those successfully executed tasks. In addition, idempotence also shows that if the playbook is corrected and executed again after the execution fails, it will not affect those tasks that have been successfully executed, even if it is a different host. In this respect alone, ansible is extremely friendly for troubleshooting. Of course, some special modules or specially defined tasks are not always idempotent. For example, the simplest one is to execute a command or shell module command, which will be executed repeatedly. But there are also ways to make it idempotent. Taking the command and shell modules as examples, they have two options: creates and removes, which respectively indicate that the command will not be executed when the specified file exists (does not exist) on the controlled host.

12.4.2. Define the details of the task

  • (1). You can add a name item to each task, or multiple tasks can depend on a name.
    For example, the following two examples. It can be seen from the two examples that the two tasks actually belong to the same name, and the second task does not need to be named with a name.

Example one:

tasks: 
    - name: do something to initialize mariadb
      file: path=/mydata/data state=directory owner=mysql group=mysql mode=0755
    - shell: /usr/bin/mysql_install_db --datadir=/mydata/data --user=mysql creates=/mydata/data/ibdata1

Example two:

tasks: 
    - name: echo var passed by nginx 
      shell: echo "{
    
    { hi_var }}"
      register: var_result
    - debug: msg="{
    
    { var_result.stdout }}"

In fact, name is just a descriptive statement, which can be defined anywhere. For example, defined at the top of play.

---
- name: start a play
 hosts: localhost
 tasks:

(2) Since it is a task, there must be one or more tasks to be performed, and its essence is to load and execute the corresponding modules of ansible. In the playbook, each module called is called an action.

For example, to define a task to ensure that the service is started, there are the following three pass module parameters:

tasks:
   - name: be sure the sshd is running
service: name=sshd state=started         # 方法一:定义为key=value,直接传递参数给模块

service:
    name: sshd
    state: started

service:
args:
   name: sshd
   state: started

But it should be noted that the ping module, command and shell modules do not need the key=value format. For the ping command, key=value can be omitted directly. For command and shell, you only need to give the command path and the options or parameters to be connected, and the above method 2 cannot be used. For example, the following definition is an ntpdate command, you only need to give its parameters.

tasks:
   - name: execute command ntpdate
shell: /usr/sbin/ntpdate ntp1.aliyun.com
   - name: ping host
    ping: 

For command or shell modules, it is sometimes necessary to consider the return status code of the command. If you want to ignore the non-zero status code and continue to execute the task, you can use the following two methods:

tasks: 
    - name: ignore non_zero return code
      shell: /usr/sbin/ntpdate ntp1.aliyun.com || /bin/true

or

tasks: 
    - name: another way to ignore the non_zero return code
      shell: /usr/sbin/ntpdate ntp1.aliyun.com
      ignore_errors: true

(3). If there are too many key=values ​​of the action, resulting in too long content, you can continue to indent on the basis of the indentation level of the previous line to indicate the continuation line.

For example, owner below is indented 4 spaces more than src.

tasks:
  - name: Copy ansible inventory file to client
    copy: src=/etc/fstab dest=/tmp
              owner=root group=root mode=0644

(4). In the value part of the action, you can refer to the defined variable, which can be a defined custom variable, or a built-in variable. See below for variables.
(5). Use the include command to include other playbook files into this playbook file. The way of Include is as follows:

12.4.3. notify and handler

Almost all modules in ansible are idempotent, which means that whether the state of the controlled host changes can be captured, that is, changed=true or changed=false for each task. When ansible captures changed=true, it can trigger the notify component (if the component is defined).

notify is a component, not a module, it can directly define action, and its main purpose is to call handler. For example:

tasks: 
    - name: copy template file to remote host
      template: src=/etc/ansible/nginx.conf.j2 dest=/etc/nginx/nginx.conf
      notify: 
        - restart nginx
        - test web page
      copy: src=nginx/index.html.j2 dest=/usr/share/nginx/html/index.html
      notify: 
        - restart nginx

This means that when the task of the template module is executed, if changed=true is caught, then notify will be triggered, and if the distributed index.html changes, nginx will also be restarted (of course, this is not necessary). Two handlers to be called are defined under notify. Handlers are mainly used to restart services or trigger system restarts, and rarely use handlers otherwise. The following are the contents of these two handlers:

handlers:
   - name: restart nginx
service: name=nginx state=restarted
   - name: test web page
    shell: curl -I http://192.168.100.10/index.html | grep 200 || /bin/false

The definition of handler is exactly the same as that of tasks, the only limitation is that the name of task in handler must be the same as the name defined in notify.

Note that notify is triggered after all tasks in a play are executed, and it will only be triggered once in a play. It means that if changed=true occurs in multiple tasks in a play, it will only be triggered once. For example, in the above example, if both the configuration file and index.html are copied to nginx, if changes occur, both will trigger the apache restart operation. But it will only restart once after the play is executed to avoid redundant restarts.

12.4.4. tag

Each task in the playbook can be tagged. The main function of the tag is to set which tagged tasks to execute or ignore tagged tasks in ansible-playbook.

tasks: 
   - name: make sure apache is running
     service: name=httpd state=started
     tags: apache
   - name: make sure mysql is running
     service: name=mysqld state=started
     tags: mysql

The following are the options for the ansible-playbook command about tags:

--list-tags                 # list all available tags
-t TAGS,  --tags=TAGS      # only run plays and tasks tagged with these values
--skip-tags=SKIP_TAGS      # only run plays and tasks whose tags do not match these values

12.5.include和roles

If all the plays are written in a playbook, it is easy to cause the playbook file to become bloated and difficult to read. Therefore, you can write multiple different tasks in different playbooks, and then use include to include them. The role is the way to integrate the playbook. Whether it is include or role, its purpose is to divide a large playbook and reuse some fine-grained plays or even tasks.

12.5.1.include

You can write the task list and handlers independently in other files, and then use include to include them in a playbook file. In addition, you can also write an independent playbook file and use include to include this file.

That is to say, include can import two kinds of files: import task, import playbook.

1. One is a task list-style file (without tasks or handlers directives), which can only be included in the sub-options of the tasks or handlers directives. This way you can pass variables to included files.

Suppose the content of a task list file /yaml/a.yaml is as follows:

---
- name: execute ntpdate
 shell: /usr/sbin/ntpdate ntp1.aliyun.com

There is a playbook named test.yaml under the same directory/yaml (except role, all relative paths in the playbook are based on the playbook), use include to include it in this playbook, if you use a relative path, the same directory will be included under the file.

---
  - hosts: centos7
    tasks:
      - include: a.yaml sayhi="hello world"

or:

---
- hosts: centos7
 tasks:
- include: a.yaml
    vars:
       sayhi: "hello world"

This variable can then be used in the included file a.yaml. For example:

---
- name: execute ntpdate
 shell: /usr/sbin/ntpdate ntp1.aliyun.com
- name: say hi to world
 debug: msg=”{
    
    {
    
     sayhi }}

Executing the test.yaml will output the corresponding variable value.

ansible-playbook /yaml/test.yaml

PLAY [centos7] **************************************************************************

TASK [Gathering Facts] ******************************************************************
ok: [192.168.100.64]
ok: [192.168.100.63]
ok: [192.168.100.65]

TASK [execute ntpdate] *******************************************************************
changed: [192.168.100.64]
changed: [192.168.100.65]
changed: [192.168.100.63]

TASK [sayhi to world] *******************************************************************
ok: [192.168.100.63] => {
    
    
    "changed": false, 
    "msg": "hello world"
}
ok: [192.168.100.65] => {
    
    
    "changed": false, 
    "msg": "hello world"
}
ok: [192.168.100.64] => {
    
    
    "changed": false, 
    "msg": "hello world"
}

PLAY RECAP ******************************************************************************
192.168.100.63             : ok=3    changed=1    unreachable=0    failed=0   
192.168.100.64             : ok=3    changed=1    unreachable=0    failed=0   
192.168.100.65             : ok=3    changed=1    unreachable=0    failed=0

2. The other is to include the entire playbook file, that is, the action of include is to load one or more plays, so it is written at the level of the top-level list.

- name: this is a play at the top level of a file
  hosts: all
  remote_user: root

  tasks:

  - name: say hi
    tags: foo
    shell: echo "hi..."

- include: load_balancers.yml  sayhi="hello world"
- include: webservers.yml
- include: dbservers.yml

    any other operations

It should be noted that in version 2.4 of ansible, two import methods, includes and imports, are added, which support more detailed static and dynamic imports, while the include statement of ansible 2.3 and before has been abandoned, but it is still available. Relevant official manual address (https://docs.ansible.com/ansible/latest/playbooks_reuse_includes.html).

12.5.2.roles

Roles means roles, which are mainly used to encapsulate playbooks to achieve reusability. In ansible, roles are represented by the organizational structure of files.

For a role, its file organization structure is shown in the following figure:
insert image description here

First, there needs to be a roles directory. At the same time, in the directory where the roles directory is located, there must also be a playbook file, here is nginx.yml, the nginx.yml file is the file that ansible-playbook needs to execute, and the role is defined in this file. When the role is executed, The relevant files will be found in the corresponding role directory in roles.

The subdirectories in the roles directory are each role. For example, there is only one role named nginx here, and in the role directory, there are several directories with fixed names (ignore if none). In these directories, there must also be some files with fixed names. Except for files with fixed names, other files can be named freely. The meaning of each directory is as follows:

tasks directory: stores the task list. If the role is to take effect, this directory must have a main task file main.yml, in main.yml you can use include to include other files in the same directory (ie tasks).
handlers directory: The directory where handlers are stored. To take effect, the file must be named main.yml.
files directory: When executing the copy or script module in the task, if a relative path is used, it will search for the corresponding file in this directory.
templates directory: When executing the template module in the task, if a relative path is used, it will search for the corresponding module file in this directory.
vars directory: Define variables specific to this role. If there is a var file, it must be a main.yml file.
defaults directory: defines the role default variables, the role default variables have the lowest priority, and will be overwritten by variables with the same name at any other level. If there is a var file, it must be a main.yml file.
meta directory: used to define role dependencies, if there are role dependencies, the file must be main.yml.

Therefore, the file organization structure of a relatively complete role is as follows:
insert image description here

If there are multiple roles, define multiple inbound (similar to the main function of C language) files (such as nginx.yml above) in the same-level directory of roles, and create the corresponding role directory in the roles directory. .
insert image description here

Of course, if the relative path is not used, then the file structure of the role does not matter, but the role function was developed to solve the problem of file confusion and bloated playbook. So if possible, try to use the recommended role file structure.

In addition, if the task, var, handler, etc. appearing in the role conflict with the object defined separately, the content in the role will be executed first.

The following is the content of the inbound file nginx.yml for the nginx role.

---
- hosts: centos7
 roles:
   - nginx

For more detailed role usage and organizational structure, see the example below.

12.6.roles example: batch automation installation

The following is an example of automatically installing nginx and mysql (CentOS 6) or maridb (CentOS 7) in batches using roles. Since the controlled node has two operating systems, CentOS 6 and CentOS 7, in addition to selecting the corresponding database, it is also necessary to adapt the nginx configuration file to each operating system, because nginx configures the content on these two versions of the system different. Here, nginx, mysql and mariadb are 3 roles, and make nginx role depend on mysql or mariadb role.

In the following example, there are some unreasonable or redundant behaviors, but as a learning example, you can have a good understanding of the organizational structure and related operations between roles.

The first is the structure of the file:
insert image description here
insert image description here

Among them, site.yml is an inbound file, which is used to call the three roles of nginx, mysql, and mariadb. The contents of this file are as follows. It has three functions:
(1) Before calling roles, configure the yum source (pre_tasks) according to the release version;
(2) Call nginx role, where the two roles of mysql and mariadb are not called, because in the nginx role The nginx role defined in the meta/main.yml file depends on these two roles, so there is no need to define it here;
(3) After executing the nginx role, output a prompt message (post_tasks).

cat /yaml/site.yml
---
  - hosts: centos
    remote_user: root

 # 根据发行版配置好yum源,使用when进行条件判断   
    pre_tasks: 
        - name: config the yum repo for centos 7
          yum_repository:
              name: epel
              description: epel
              baseurl: http://mirrors.aliyun.com/epel/7/$basearch/
              gpgcheck: no
          when: ansible_distribution_major_version == "7"

        - name: config the yum repo for centos 6
          yum_repository:
              name: epel
              description: epel
              baseurl: http://mirrors.aliyun.com/epel/6/$basearch/
              gpgcheck: no
          when: ansible_distribution_major_version == "6"

    roles: 
        - nginx

# 输出over消息
    post_tasks:
      - shell: echo 'deploy nginx/mysql over'
        register: ok_var
      - debug: msg='{
    
    { ok_var.stdout }}'

The following are the contents of each file in nginx role. The source files copied by template are all extracted from centos 6 nginx and centos 7 nginx, but they are just renamed.

/yaml/roles/nginx/tasks/main.yml 
---
   - name: make sure nginx state is installed
     yum: name=nginx state=installed

   - name: template nginx.conf
# 基于变量赋值配置文件模板,检查配置文件语法,并在必要的时候触发handler
     template: src=nginx{
    
    {
    
     ansible_distribution_major_version }}.conf.j2
                  dest=/etc/nginx/nginx.conf
                  validate="/usr/sbin/nginx -t -c %s"  
     notify: 
        - restart nginx

# 基于jinja2渲染模板文件,且改变时也触发重启操作
   - name: copy index.html
     template: src=index.html.j2 dest=/usr/share/nginx/html/index.html
     notify: 
        - restart nginx

   - name: make sure nginx service is running
     service: name=nginx state=started

# 引用变量 nginx_port,在vars/main.yml中定义了
   - name: make sure port is open
     wait_for: port="{
    
    { nginx_port }}"

/yaml/roles/nginx/handlers/main.yml 
---
   - name: restart nginx
     service: name=nginx state=restarted

/yaml/roles/nginx/vars/main.yml 
nginx_port: 80

# 定义nginx依赖于MySQL或mariadb,具体依赖于哪个,是通过条件进行判断的,centos 6表示依赖于mysql,centos 7表示依赖于mariadb
# 同时传递了两个值给变量hi_var,由于是在依赖的时候传递的,所以这两个变量可直接在依赖的role(mysql role或mariadb role)的playbook中引用
/yaml/roles/nginx/meta/main.yml 
---
   dependencies: 
     - {
    
     role: mysql,hi_var: "hello mysql",when: "ansible_distribution_major_version == '6'" }
     - {
    
     role: mariadb,hi_var: "hello mariadb",when: "ansible_distribution_major_version == '7'" }

Index.html.j2 is copied above, and its content is:

shell> cat /yaml/roles/nginx/templates/index.html.j2 
<h1>hello from {
    
    {
    
     ansible_default_ipv4.address }}<h1>

When the template is executed, it will use the jinja2 engine to replace the variables in the file, so that when copying to different hosts, the content of the index.html is based on the remote host ip. The variable used here is the variable "ansible_default_ipv4.address" in the collected facts.

The following are the contents of each file in the mysql role. Note that MySQL's my.cnf and mariadb's my.cnf are not the same by default (mariadb's my.cnf has an additional configuration "!includedir /etc/my.cnf.d" by default, and MySQL needs to cancel this item) , so they need to be provided separately.

/yaml/roles/mysql/tasks/main.yml 
---
    - name: make sure mysql is installed
      yum: name=mysql-server state=installed

# 特别需要注意下面的初始化命令,由于执行的是shell模块,所以要考虑其幂等性,显然初始化动作是一定要实现幂等性的
    - name: do something to initialize mysql
      file: path=/mydata/data state=directory owner=mysql group=mysql mode=0755
    - shell: /usr/bin/mysql_install_db --datadir=/mydata/data --user=mysql creates=/mydata/data/ibdata1

    - name: copy my.cnf
      copy: src=my.cnf dest=/etc/my.cnf
      notify: 
         - restart mysql

    - name: make sure mysql is running
      service: name=mysqld state=started

    - name: make sure mysql port is open
      wait_for: 
         port: "{
    
    { mysql_port }}"
         timeout: 10

# 这里输出了nginx/meta/main.yml中传递的变量
    - name: echo var passed by nginx 
      shell: echo "{
    
    { hi_var }}"
      register: var_result
    - debug: msg="{
    
    { var_result.stdout }}"

/yaml/roles/mysql/handlers/main.yml 
---
   - name: restart mysql
     service: name=mysqld state=restarted

/yaml/roles/mysql/vars/main.yml 
---
    mysql_port: 3306

以下是mariadb role中各文件的内容,和mysql大体上是一致的。注意,MySQL的my.cnf和mariadb的my.cnf默认情况下并不一样,所以需要分别提供。
/yaml/roles/mariadb/tasks/main.yml 
---
    - name: make sure mariadb is installed
      yum: name=mariadb-server state=installed

    - name: do something to initialize mariadb
      file: path=/mydata/data state=directory owner=mysql group=mysql mode=0755
    - shell: /usr/bin/mysql_install_db --datadir=/mydata/data --user=mysql creates=/mydata/data/ibdata1

    - name: copy my.cnf
      copy: src=my.cnf dest=/etc/my.cnf
      notify: 
         - restart mariadb

    - name: make sure mariadb is running
      service: name=mariadb state=started

    - name: make sure mariadb port is open
      wait_for: 
         port: "{
    
    { mariadb_port }}"
         timeout: 10

    - name: echo var passed by nginx 
      shell: echo "{
    
    { hi_var }}"
      register: var_result
    - debug: msg="{
    
    { var_result.stdout }}"

/yaml/roles/mariadb/handlers/main.yml 
---
    - name: restart mariadb
      service: name=mariadb state=restarted

/yaml/roles/mariadb/vars/main.yml 
---
    mariadb_port: 3306

The following are the test results of two machines, one is centos 7 (192.168.100.54) and the other is centos 6 (192.168.100.70).

PLAY [newhosts] *************************************************************************

TASK [Gathering Facts] ******************************************************************
ok: [192.168.100.70]
ok: [192.168.100.54]

TASK [config the yum repo for centos 7] *************************************************
skipping: [192.168.100.70]
changed: [192.168.100.54]

TASK [config the yum repo for centos 6] *************************************************
skipping: [192.168.100.54]
changed: [192.168.100.70]

TASK [mysql : make sure mysql is installed] *********************************************
skipping: [192.168.100.54]
changed: [192.168.100.70]

TASK [mysql : do something to initialize mysql] *****************************************
skipping: [192.168.100.54]
changed: [192.168.100.70]

TASK [mysql : command] ******************************************************************
skipping: [192.168.100.54]
changed: [192.168.100.70]

TASK [mysql : copy my.cnf] **************************************************************
skipping: [192.168.100.54]
changed: [192.168.100.70]

TASK [mysql : make sure mysql is running] ***********************************************
skipping: [192.168.100.54]
changed: [192.168.100.70]

TASK [mysql : make sure mysql port is open] *********************************************
skipping: [192.168.100.54]
ok: [192.168.100.70]

TASK [mysql : echo var passed by nginx] *************************************************
skipping: [192.168.100.54]
changed: [192.168.100.70]

TASK [mysql : debug] ********************************************************************
skipping: [192.168.100.54]
ok: [192.168.100.70] => {
    
    
    "changed": false, 
    "msg": "hello mysql"
}

TASK [mariadb : make sure mariadb is installed] *****************************************
skipping: [192.168.100.70]
changed: [192.168.100.54]

TASK [mariadb : do something to initialize mariadb] *************************************
skipping: [192.168.100.70]
changed: [192.168.100.54]

TASK [mariadb : command] ****************************************************************
skipping: [192.168.100.70]
changed: [192.168.100.54]

TASK [mariadb : copy my.cnf] ************************************************************
skipping: [192.168.100.70]
changed: [192.168.100.54]

TASK [mariadb : make sure mariadb is running] *******************************************
skipping: [192.168.100.70]
changed: [192.168.100.54]

TASK [mariadb : make sure mariadb port is open] *****************************************
skipping: [192.168.100.70]
ok: [192.168.100.54]

TASK [mariadb : echo var passed by nginx] ***********************************************
skipping: [192.168.100.70]
changed: [192.168.100.54]

TASK [mariadb : debug] ******************************************************************
skipping: [192.168.100.70]
ok: [192.168.100.54] => {
    
    
    "changed": false, 
    "msg": "hello mysql"
}

TASK [nginx : make sure nginx state is installed] ***************************************
changed: [192.168.100.54]
changed: [192.168.100.70]

TASK [nginx : template nginx.conf] ******************************************************
ok: [192.168.100.70]
changed: [192.168.100.54]

TASK [nginx : copy index.html] **********************************************************
changed: [192.168.100.70]
changed: [192.168.100.54]

TASK [nginx : make sure nginx service is running] ***************************************
changed: [192.168.100.54]
changed: [192.168.100.70]

TASK [nginx : make sure port is open] ***************************************************
ok: [192.168.100.70]
ok: [192.168.100.54]

RUNNING HANDLER [mysql : restart mysql] *************************************************
changed: [192.168.100.70]

RUNNING HANDLER [mariadb : restart mariadb] *********************************************
changed: [192.168.100.54]

RUNNING HANDLER [nginx : restart nginx] *************************************************
changed: [192.168.100.54]

TASK [command] **************************************************************************
changed: [192.168.100.54]
changed: [192.168.100.70]

TASK [debug] ****************************************************************************
ok: [192.168.100.54] => {
    
    
    "changed": false, 
    "msg": "deploy nginx/mysql over"
}
ok: [192.168.100.70] => {
    
    
    "changed": false, 
    "msg": "deploy nginx/mysql over"
}

PLAY RECAP ******************************************************************************
192.168.100.54             : ok=19   changed=14   unreachable=0    failed=0   
192.168.100.70             : ok=18   changed=12   unreachable=0    failed=0

I believe that after reading the example of roles organization above, you will have a deeper understanding of the usage and playbook of roles. In fact, ansible has a website that stores a lot of playbooks, which can be regarded as a playbook warehouse. You can download it and use it with a little modification. Even if you don’t use it, it’s worth learning from their writing methods. Address: ansible galaxy (https://galaxy.ansible.com/home)

In addition, organizing roles according to different standards may make it easier to write playbooks. For example, in the above example, it may be easier to divide roles by release version than by the type of installed software. Of course, it is also possible to divide centos 6 and centos 7 in the inventory. Which one is more convenient and reusable needs to be considered by yourself.

Guess you like

Origin blog.csdn.net/toto1297488504/article/details/132233059