• ansible的高级应用-roles


    在之前我们知道了playbook,类似于shell的脚本,playbook适用于一些不太麻烦的部署任务,比如说使用playbook安装mysql,那么我们直接写一个playbook文件即可。可是如果我们还要搭建mysql的主从架构呢?一个playbook就会显得会力不从心,这个时候我们可以使用roles。roles就是有相互关联功能的集合。相对于playbook,roles更适合于大项目的编排和架构。

    在使用roles之前,简单说明一下include的用法,在playbook中可以引入另外的playbook脚本,这时候可以使用include命令。

      -  include test.yml

    整体架构

    roles主要依赖于目录命名和摆放,默认tasks/main.yml是所有任务的入口,所以使用roles的过程可以理解为目录规范化命名的过程。roles的目录架构是确定的,如下:

    roles/                     #roles目录下面存放对应的role
    └── master_slave_mysql     #目录名以role名命名,目录下面是每个role都包含的文件,若是对应的文件没有使用可以不创建
        ├── files              #files用于文件传输,一些压缩包可以放在这里,在playbook中直接调用即可  
        ├── handlers           #role项目所有使用的handlers写在这里
        ├── tasks              #主要的逻辑文件,也就是playbook文件执行的任务 
        ├── templates          #templates也用于文件的传输,但是这里的文件可以使用jija2模板语法,而files传输的文件不能使用jija2模板语法
        └── vars               #定义变量

    #角色下面的每个文件中【files和templates目录下面不需要】,都必须需要一个main.yml文件,作为该模块的调度入口。而对应的逻辑文件可以写在对应的模块下面,
    #然后在main.yml文件中使用include语句引入。

    先来看一下一个完整role的所有目录结构:

    [root@docker5 ~]# tree roles/
    roles/
    └── master_slave_mysql
        ├── files
        │   ├── initmysql7.sh                                  #MySQL安装的初始化脚本
        │   └── mysql-5.7.22-linux-glibc2.12-x86_64.tar.gz     #MySQL安装包  
        ├── handlers
        │   ├── all_handlers.yml                               #roles所有的handlers,若是所需handlers可以分多个文件写入,
        │   └── main.yml                                       #在main.yml文件使用include语句,引入所有的handlers文件
        ├── tasks                                              #
        │   ├── add_repl_user.yml                              #添加复制用户的playbook文件
        │   ├── change_passwd.yml                              #MySQL5.7初始化后,需要修改密码,这里是修改密码的playbook文件
        │   ├── install_mysql.yml                              #安装mysql的playbook  
        │   ├── main.yml                                       #在main中引入对应的playbook,注意引入的顺序    
        │   └── set_replication.yml                            #配置主从复制的playbook 
        ├── templates
        │   ├── my.cnf.j2                                      #MySQL的配置文件模板
        │   ├── test.retry                                     #后面这两个是我测试的时候的文件,不用管
        │   └── test.yml
        └── vars                                               #定义变量
            └── main.yml                                       #定义变量文件,也可以单独写成playbook的形式,然后再引入,因为这里变量比较少,就直接写入了main.yml  
    
    6 directories, 13 files

    在和roles同级目录的结构中,我们还需要定义个yml文件,作为项目的调度入口。

    [root@docker5 ~]# cat site.yml 
    ---
     # 该项目的调度入口
     - hosts: all
       remote_user: root
    
       roles:
         - master_slave_mysql
    [root@docker5 ~]# lsroles    site.yml 

    #执行的时候直接执行入口文件即可, 注意需要定义inventory文件。 [root@docker5
    ~]# ansible-playbook site.yml

    下面我们来一个个解析这个简单的项目。

    inventory文件

    默认是读取/etc/ansible/hosts中的内容,其内容如下:【需要说明的是,这里虽然定义了多个分组,但是在后面代码中没怎么使用】

    [root@docker5 ~]# cat /etc/ansible/hosts 
    [all]
    10.0.102.212 
    10.0.102.200 
    10.0.102.162
    
    [master]
    10.0.102.162
    
    [slave]
    10.0.102.200
    10.0.102.212

    files模块

    这各模块主要用于文件的传输,这个项目中我们在里面放了一个初始化脚本和mysql5.7的安装包。初始化脚本就是一条很简单的命令,写在这里是为了说明files的用法。

    初始化脚本如下:

    [root@docker5 files]# cat !$
    cat initmysql7.sh
    #!/bin/bash
    ./bin/mysqld --user=mysql --datadir=/data/mysql --initialize
    [root@docker5 files]# 
    
    
    #在playbook中引用时,可以直接引用,而不是使用绝对路径
     - name: copy the mysql install pkg
       copy: src={{ install_pkg_name }}.tar.gz dest=/usr/local/src #src直接引用即可
    - name: init mysql script: chdir={{ basedir_name }} initmysql7.sh #执行这个脚本,而不是使用绝对路径

    handlers模块

    handlers的作用不再说明,这里只说明handlers在roles中的编排。

    [root@docker5 files]# cd ../handlers/
    [root@docker5 handlers]# ls
    all_handlers.yml  main.yml
    [root@docker5 handlers]# cat all_handlers.yml         #直接写入对应的handlers即可,
    ---
     - name: start mysql
       service: name=mysqld state=started
    
     - name: restart mysql
       service: name=mysqld state=restarted
    
     - name: flush privileges
       shell:  chdir=/usr/local/mysql/bin ./mysql  -u{{ login_user }} -p{{ login_passwd }} -e "flush privileges"
    [root@docker5 handlers]# cat main.yml 
    ---
     - include: all_handlers.yml
    ---
     - name: start mysql
       service: name=mysqld state=started
    
     - name: restart mysql
       service: name=mysqld state=restarted
    
     - name: flush privileges
       shell:  chdir=/usr/local/mysql/bin ./mysql  -u{{ login_user }} -p{{ login_passwd }} -e "flush privileges"
    all_handlers.yml
    ---
     - include: all_handlers.yml
    main.yml

    tasks模块

    tasks是roles主要的逻辑文件,所有的play都在这里执行,先来看一下main文件。

    [root@docker5 tasks]# cat  main.yml         #包含了四个playbook文件,要注意文件的逻辑顺序
    ---
     - include: 
          install_mysql.yml
          change_passwd.yml
          add_repl_user.yml
          set_replication.yml
    install_mysql.yml文件主要用来安装MySQL,拷贝配置文件并且启动mysql,内容如下。
    ---
     - name: copy the mysql install pkg
       copy: src={{ install_pkg_name }}.tar.gz dest=/usr/local/src
    
     - name: uncompress the pkg
       unarchive: src=/usr/local/src/{{ install_pkg_name }}.tar.gz dest=/usr/local/ copy=no
    
     - name: create a soft link
       #shell: ln -s /usr/local/{{ install_pkg_name }}  /usr/local/mysql
       file: src=/usr/local/{{ install_pkg_name }} dest={{ basedir_name }} state=link   
    
     - name: create the mysql user
       user: name={{ mysql_user }} 
    
     - name: create the datadir
       file: path={{ datadir_name }}  state=directory owner={{ mysql_user }} group={{ mysql_user }}
    
     - name: init mysql
       script: chdir={{ basedir_name }} initmysql7.sh
    
     - name: copy the manage script
       shell: cp -p /usr/local/mysql/support-files/mysql.server /etc/init.d/mysqld
    
     - name: change the start script
       #shell: sed -i "s/^datadir=/datadir=/data/mysql/" /etc/init.d/mysqld
       replace: path=/etc/init.d/mysqld replace="datadir={{ datadir_name }}" regexp="^datadir=" backup=yes 
    
     - name: copy the mysql config
       template: src=my.cnf.j2 dest=/etc/my.cnf 
       notify:
        - start mysql
    install_mysql.yml
    change_passwd.yml文件主要用来更改密码,因为MySQL5.7的密码属性,因此需要更改密码才能使用。这里没有使用ansible的相关模块,而是直接使用了sql命令。
    ---
     - name: 设置数据库密码
       shell: chdir={{ mysql_path }} ./mysql   -e "update mysql.user set authentication_string=password("123456")" 
    
     - name: 开启权限认证
       replace: 
          path=/etc/my.cnf
          regexp="^skip-grant-tables$"
          replace="#skip-grant-tables"
       notify:
          restart mysql
    
     - name: 设置密码
       shell: chdir={{ mysql_path }} ./mysql -uroot  -p123456 --connect-expired-password  -e 'alter user "root"@"localhost" identified by "123456"'
    change_passwd.yml
    add_repl_user.yml文件用来在master上添加复制用户,同样的也没有使用ansible相关的模块,而是使用了sql命令。
    ---
      - name: add the user to replication
        shell: chdir=/usr/local/mysql/bin ./mysql   -uroot -p123456 -e 'grant all privileges on *.* to "repl"@"%" identified by "123456"'
        notify:
           flush privileges
        when: ansible_eth0.ipv4.address  == "10.0.102.162"
    add_repl_user.yml
    set_replication.yml文件用来在从上设置复制步骤,然后开启复制,这里使用了mysql_replication的模块。因为这里是全新的mysql,因此在change master时没有指定二进制日志名和日志位置点。
    ---
     - name: Get the current master servers replication status 
       mysql_replication:
          login_user=root
          login_password=123456
          login_unix_socket=/tmp/mysql.sock
          mode=getmaster
       register: repl_stat
       when: ansible_eth0.ipv4.address  == "10.0.102.162"
    
    
     - name: test
       mysql_replication:
          login_user={{ login_user }}
          login_password={{ login_passwd }}
          login_unix_socket={{ sock_path }}
          mode=changemaster
          master_host="10.0.102.162"
          master_user="repl"
          master_password="123456"
       when: ansible_eth0.ipv4.address  != "10.0.102.162"
    
     - name: start slave in slave to start the replication
       mysql_replication:
          login_user=root
          login_password=123456
          login_unix_socket=/tmp/mysql.sock
          mode=startslave
       when: ansible_eth0.ipv4.address  != "10.0.102.162"
    set_replication.yml

    这个文件中的when语句可以换位委托语句delegate_to。另外需要说明的是when语句引用变量时会报如下错误:

     [WARNING]: when statements should not include jinja2 templating delimiters such as {{ }} or {% %}. Found: ansible_eth0.ipv4.address  == {{ master_host }}
    
    fatal: [10.0.102.200]: FAILED! => {"msg": "The conditional check 'ansible_eth0.ipv4.address  == {{ master_host }}' failed. The error was: error while evaluating conditional (ansible_eth0.ipv4.address  == {{ master_host }}): float object has no element 102
    
    The error appears to have been in '/root/roles/master_slave_mysql/tasks/set_replication.yml': line 2, column 4, but may
    be elsewhere in the file depending on the exact syntax problem.
    
    The offending line appears to be:
    
    ---
     - name: Get the current master servers replication status
       ^ here
    "}

    解决办法:http://blog.sina.com.cn/s/blog_704836f40102xbkt.html

    就是把when后面的语句先用单引号括起来,里面对应的变量再使用双引号。

    在这里应用的时候还有一个问题,如果在changge master的时候指定日子文件名和日志位置点,如下!我自己调用的时候总是报错。

     - name: Change the master in slave to start the replication
       mysql_replication:
          login_user=root
          login_password=123456
          login_unix_socket=/tmp/mysql.sock
          master_log_file={{ repl_stat.File }}                        #加上这两句,再执行的时候总是报错
          master_log_pos={{ repl_stat.Position }}
          mode=changemaster
          master_host="10.0.102.162"
          master_user="repl"
          master_password="123456"
       when: ansible_eth0.ipv4.address != "10.0.102.162"
    
     - name: start slave in slave to start the replication
       mysql_replication:
          login_user=root
          login_password=123456
          login_unix_socket=/tmp/mysql.sock
          mode=startslave
       when: ansible_eth0.ipv4.address != "10.0.102.162"

    报错提示如下:【求哪位知道怎么解决了,告诉一下】

    fatal: [10.0.102.200]: FAILED! => {"msg": "The task includes an option with an undefined variable. The error was: 'dict object' has no attribute 'File'
    
    The error appears to have been in '/root/roles/master_slave_mysql/tasks/set_replication.yml': line 16, column 4, but may
    be elsewhere in the file depending on the exact syntax problem.
    
    The offending line appears to be:
    
    
     - name: test
       ^ here
    "}
    报错提示

    但是因为这里我们的mysql都是全新安装的,因此不需要指定日志名和日志位置掉,因此不需要加入这两句,也就没有报错。

    templates模块

    这个模块与files模块都可以用于文件传输,最大的不同是,templates可以使用jija2语法。譬如这里,我们使用这个模块传输mysql的配置文件,我们知道在主从集群中在主上需要开通二进制日志,并且三个服务器的server_id要不一样。这个时候如果我们使用files模块就需要三个不同的配置模板,而是用templates模块,则只需要一个配置模板即可。

    [root@docker5 templates]# cat my.cnf.j2
    [mysqld]
    datadir={{ datadir_name }}
    socket={{ sock_path }}
    symbolic-links=0
    skip-grant-tables
    server_id={{ ansible_eth0.ipv4.address| regex_search("([0-9]{1,3})$") }}
    
    {% if ansible_eth0.ipv4.address == master_host %}
    log-bin=
    {% endif %}
    
    [mysqld_safe]
    log-error={{ datadir_name }}/{{ ansible_hostname }}.err
    pid-file={{ datadir_name }}/mysql.pid

    在templates模板中可以使用定义变量,在这个模板中我们定义了server_id为每个每个服务器ip地址的最后一部分【如果服务器跨网段,那么这个数字可能重复,但是这里是在同一网段的,因此数字是唯一的】

    使用了if条件判断,判断若是当前主机ip和master对应主机相等,则设置log-bin参数,否则不设置。这就是一个简单的模板。

    vars模块

    vars模板主要是写入roles中定义的变量,当然变量也可以定义在inventory文件中。定义的变量最好加上注释性的说明,这样方便后续更改。

    ---
    
    #数据库安装包的名字
    install_pkg_name: mysql-5.7.22-linux-glibc2.12-x86_64
    
    #初始化脚本的名字
    init_mysql: initmysql.sh
    
    #mysql用户名
    mysql_user: mysql
    
    #数据库数据目录
    datadir_name: /data/mysql
    basedir_name: /usr/local/mysql
    
    #mysql命令的绝对路径
    mysql_path: /usr/local/mysql/bin/
    
    #连接mysql的变量
    login_user: root
    login_passwd: 123456
    sock_path: /tmp/mysql.sock
    
    #服务器主机变量
    master_host: "10.0.102.162"
    slave1: "10.0.102.200"
    slave2: "10.0.102.212"

    设置完如上的文件之后,一个简单的roles已经完成。接下来直接只要设置好ssh认证,那么久可以执行。

  • 相关阅读:
    封装/接口/抽象类
    继承
    浅谈面向对象
    MySQL---Mybatis 批处理(增,改,删)
    dashboard安装
    K8S--------常用命令
    K8S------概述
    Java实现 MD5加盐加密 和 MD5和SHA-1混合加盐加密
    使用FastJson对JSON字符串、JSON对象及JavaBean之间的相互转换
    SpringCloud---FeignClient处理请求超时问题
  • 原文地址:https://www.cnblogs.com/wxzhe/p/10418056.html
Copyright © 2020-2023  润新知