第七节:Ansible 剧本重用与解耦

一、剧本重用与解耦

在大型项目中,涉及到的功能模块多且杂,你不可能全部放入一个playbook中
为了方便管理,我们必须
1、开发的时候

  • 要把不同的功能放入不同的文件中实现解耦合吗
  • 一些文件可以被共享引用实现复用

2、执行的时候:最好能统一在一起一键执行

实现方式有两种
1、include功能
2、基于roles进行工程化开发

Ansible 的 roles 是一种结构化的方式,用于组织任务、模板、文件和变量。
此结构使管理复杂的自动化设置更为简单,因为与特定角色相关的所有内容都包含在其目录中
它就跟你开发一个django项目,不同的文件放入不同的目录里一样的道理

二、include使用示例

2.1 使用inclulde_tasks引入存放task的文件

1)把两个小task放入不同的文件,注意每个小task的文件是无法单独运行的

cat > play1.yml << "EOF"
- name: task1
  copy:
    content: "111"
    dest: "/tmp/1.txt"
EOF

cat > play2.yml << "EOF"
- name: task2
  copy:
    content: "222"
    dest: "/tmp/2.txt"
EOF

2)用include_tasks指令来引入task文件

[root@m01 project]# vim main.yml
- hosts: group5
  tasks:
    - include_tasks: /workspace/play1.yml
    - include_tasks: /workspace/play2.yml

2.2 使用import_playbook来直接复用一整个剧本

每个playbook文件都是一个可以独立运行的剧本,如果你想复用的是一个整个剧本,那需要使用import_playbook指令

[root@m01 project]# cat main.yml 
- import_playbook: ./base.yml
- import_playbook: ./nginx.yml
- import_playbook: ./php.yml
- import_playbook: ./wordpress.yml
- import_playbook: ./mariadb.yml

三、Ansible Roles

3.1 Roles基本概述

通过使用 roles,你可以将复杂的设置和操作流程封装在一个统一的文件夹下,进而实现代码的复用和模块化
例如你要部署负载均衡、web服务器、nfs、数据库,那你可以创建四个role
每个role都组织管理了好了各自需要的所有元素(包括任务、变量、handler、文件等)的目录结构。

3.2 创建单独一个Role

一个完整的Role里包含的目录和文件可能较多,手动去创建所有这些目录和文件是一件比较烦人的事,好在可以使用ansible-galaxy init ROLE_NAME命令来快速创建一个符合Role文件组织规范的框架。

[root@lb workspace]# ansible-galaxy init first_role

[root@lb workspace]# tree first_role/
first_role/       # 角色名称,或者叫项目名
├── README.md
├── defaults      # 默认的变量(优先级很低)
│   └── main.yml
├── files         # 存放文件,使用copy模块时自动获取
├── handlers      # 存放触发器的配置
│   └── main.yml
├── meta          # 依赖的服务,执行该项目时先执行其他的项目
│   └── main.yml
├── tasks         # 默认执行的playbook
│   └── main.yml
├── templates     # 存放jinja2模板,使用template模块时自动获取
├── tests
│   ├── inventory
│   └── test.yml
└── vars          # 存放变量
    └── main.yml

3.3 一些关键介绍

一、Ansible Roles的依赖关系说明

`roles`允许你再使用roles时自动引入其他的roles。role依赖关系存储在roles目录中meta/main.yml文件中。

例如:推送wordpress并解压,前提条件,必须要安装nginx和php,把服务跑起来,才能运行wordpress的页面,此时我们就可以在wordpress的roles中定义依赖nginx和php的roles
[root@m01 roles]# vim /etc/ansible/roles/wordpress/meta/main.yml
dependencies:
  - { role: nginx }
  - { role: php }

如果编写了meta目录下的main.yml文件,那么Ansible会自动先执行meta目录中main.yml文件中的dependencies文件,如上所示,就会先执行nginx和php的安装。

二、Role中有两个地方可以定义变量:

  • (1).roles/xxx/defaults/main.yml:用于定义Role的默认变量
  • (2).roles/xxx/vars/main.yml :用于定义其它变量
    这两个文件之间的区别在于,defaults/main.yml中定义的变量优先级低于vars/main.yml中定义的变量。事实上,defaults/main.yml中的变量优先级几乎是最低的,基本上其它任何地方定义的变量都可以覆盖它。

3.4、基于roles机制重构playbook

基于roles机制将https://egonlin.com/?p=355第三小节的playbook进行重构

file

你可以git clone https://gitee.com/egonlin/ansible-playbook-test.git
把之前我们写的这个剧本拉到本地,然后用roles实现重构

1.配置主机清单

[root@manager ~]# cat /etc/ansible/hosts
[lb]
lb01 ansible_ssh_pass='1'

[nfs_server]
nfs ansible_ssh_pass='1'

[web_group]
web01 ansible_ssh_pass='1'
web02 ansible_ssh_pass='1'

[db_server]
db01 ansible_ssh_pass='1'

[www:children]
lb
nfs_server
web_group
db_server

2.配置hosts

[root@manager ~]# cat /etc/hosts # 添加如下内容

192.168.71.12 lb01
192.168.71.13 nfs
192.168.71.14 web01
192.168.71.15 web02
192.168.71.16 db01

3、创建项目及各个role

# 1、创建项目目录,项目名就叫roles吧,好理解
mkdir /project
mkdir /project/roles

# 2、在roles目录下创建一系列的role
cd /project/roles          # 切换到工作目录下
ansible-galaxy init base   # 基础优化role
ansible-galaxy init nfs    # 部署nfs服务的role
ansible-galaxy init web    # 部署web服务的role
ansible-galaxy init mysql  # 部署mysql服务的role
ansible-galaxy init lb     # 部署lb负载均衡服务的role
ansible-galaxy init wordpress     # 部署wordpress项目的role

4、base role

既然是tasks文件,那必然是只放tasks下的内容就好了,你在tasks/main.yml里再出现如下层级是错误的
- hosts: all
  tasks:
    - name: 任务1
      ...
    - name: 任务2
      ...

直接把任务1、任务2摘出来放入tasks/main.yml即可,后面都是如此
[root@m01 roles]# cat base/tasks/main.yml 
- name: Stop Selinux
  selinux:
    state: disabled

- name: Stop Firewalld
  systemd:
    name: firewalld
    state: stopped

- name: Create www Group
  group:
    name: www
    gid: 1666
    state: present

- name: Create www User
  user:
    name: www
    uid: 1666
    group: www
    shell: /sbin/nologin
    create_home: false
    state: present

5、nfs role

1、准备文件

[root@m01 roles]# cat nfs/files/exports.txt
/data 192.168.71.0/24(rw,sync,all_squash)

2、编写tasks

[root@m01 roles]# cat nfs/tasks/main.yml 
- name: Install nfs-util
  yum:
    name: nfs*
    state: present

- name: mkdir /data
  file:
    path: "/data"
    state: directory

- name: Config nfs
  copy:
    src: "exports.txt" # 直接引用文件即可
    dest: /etc/exports

- name: Start nfs-server
  systemd:
    name: nfs-server
    state: restarted
    enabled: true

6 web role

部署两台web上的nginx+php环境,并且挂载好nfs共享存储

1)准备包和配置文件

cd /project/roles
1、nginx的安装源文件

cat > web/files/nginx.repo << "EOF"
[nginx-stable]
name=nginx stable repo
baseurl=http://nginx.org/packages/centos/$releasever/$basearch/
gpgcheck=1
enabled=1
gpgkey=https://nginx.org/keys/nginx_signing.key
module_hotfixes=true

[nginx-mainline]
name=nginx mainline repo
baseurl=http://nginx.org/packages/mainline/centos/$releasever/$basearch/
gpgcheck=1
enabled=0
gpgkey=https://nginx.org/keys/nginx_signing.key
module_hotfixes=true

EOF

2、nginx转发php-fpm的配置

cat > web/files/myweb.conf << "EOF"
server {
    listen       8181;
    server_name  localhost;
    location / {
        root   /usr/share/nginx/html;
        index index.php index.html;
    }
    location ~ \.php$ {
        fastcgi_pass   127.0.0.1:9000;
        fastcgi_param  SCRIPT_FILENAME /usr/share/nginx/html$fastcgi_script_name;
        fastcgi_param HTTPS on;
        include        fastcgi_params;
    }
}
EOF

3、准备好php-fpm的配置文件

cat > web/files/www.conf << "EOF"
[www]
user = www
group = www
listen = 127.0.0.1:9000
listen.allowed_clients = 127.0.0.1
pm = dynamic
pm.max_children = 50
pm.start_servers = 5
pm.min_spare_servers = 5
pm.max_spare_servers = 35
slowlog = /var/opt/remi/php74/log/php-fpm/www-slow.log
php_admin_value[error_log] = /var/opt/remi/php74/log/php-fpm/www-error.log
php_admin_flag[log_errors] = on
php_value[soap.wsdl_cache_dir]  = /var/opt/remi/php74/lib/php/wsdlcache
EOF

4、查看如下

[root@m01 roles]# ls web/files/
myweb.conf  nginx.repo  www.conf

2)编写部署php-fpm+nginx的剧本

[root@m01 roles]# cat web/tasks/main.yml 
# 1、安装php
- name: Gather OS version
  command: cat /etc/redhat-release
  register: os_version

- name: Extract major version from OS version
  set_fact:
    os_major_version: "{{ os_version.stdout.split()[3] | regex_replace('\\..*$', '') }}"
- name: Extract minor version from OS version
  set_fact:
    os_minor_version: "{{ os_version.stdout.split()[3] | regex_replace('^[^.]*\\.', '') }}"

- name: Install rpm package for CentOS 7.9
  yum:
    name: http://rpms.remirepo.net/enterprise/remi-release-7.rpm
    state: latest
  when: os_major_version == '7' and os_minor_version == '9.2009'

- name: Install rpm package for CentOS 9.3
  yum:
    name: http://rpms.remirepo.net/enterprise/remi-release-9.3.rpm
    state: latest
  when: os_major_version == '9' and os_minor_version == '3'

- name: Install php-fpm
  yum:
    name: 
      - 'php74-php-pdo'
      - 'php74-php-mbstring'
      - 'php74-php-cli'
      - 'php74-php-fpm'
      - 'php74-php-mysqlnd'
    state: latest

- name: Config php-fpm
  copy:
    src: www.conf
    dest: /etc/opt/remi/php74/php-fpm.d/www.conf
  notify: restart_php

- name: Start php-fpm
  systemd:
    name: php74-php-fpm
    state: restarted
    enabled: true

#2、安装nginx,配置nginx代理php-fpm
- name: copy nginx.repo
  copy:
    src: nginx.repo
    dest: /etc/yum.repos.d/nginx.repo

- name: Install nginx
  yum:
    name: nginx
    state: latest

- name: Config nginx
  copy:
    src: myweb.conf
    dest: /etc/nginx/conf.d/myweb.conf
  notify: restart_nginx

- name: Start nginx server
  systemd:
    name: nginx
    state: restarted
    enabled: true

#3、配置所有web服务挂载nfs
- name: 安装nfs
  yum:
    name: nfs-utils
    state: latest

- name: 挂载
  mount:
    path: /usr/share/nginx/html
    src: "{{ nfs_share_dir }}"
    fstype: nfs
    opts: defaults
    state: mounted

配置触发器handler

[root@m01 roles]# cat web/handlers/main.yml 
- name: restart_php
  systemd:
    name: php74-php-fpm
    state: restarted

- name: restart_nginx
  systemd:
    name: nginx
    state: restarted

创建变量

[root@m01 roles]# cat web/vars/main.yml
nfs_share_dir: "192.168.71.13:/data"

7 mysql role

安装mysql_db模块

ansible-galaxy collection install community.mysql

编写剧本

[root@m01 roles]# cat mysql/tasks/main.yml 
- name: Download PyMySQL tar.gz
  get_url:
	url: https://files.pythonhosted.org/packages/44/39/6bcb83cae0095a31b6be4511707fdf2009d3e29903a55a0494d3a9a2fac0/PyMySQL-0.8.1.tar.gz
	dest: /tmp/PyMySQL-0.8.1.tar.gz

- name: Extract PyMySQL tar.gz
  unarchive:
	src: /tmp/PyMySQL-0.8.1.tar.gz
	dest: /tmp/
	remote_src: yes

- name: Install PyMySQL
  command:
	cmd: "python setup.py install"
	chdir: "/tmp/PyMySQL-0.8.1"
	


- name: clear mysql
  shell: "yum remove mysql* -y"
  ignore_errors: True
 
- name: Install mariadb
  yum:
    name: mariadb*
    state: latest
 
- name: init maridb
  shell: "rm -rf /var/lib/mysql/*"
 
- name: Start mariadb
  systemd:
    name: mariadb
    state: restarted
    enabled: true
 
 
- name: create database
  mysql_db:
    # root登录localhost不允许,需要用套接字登录
    login_unix_socket: /var/lib/mysql/mysql.sock
    # 引用变量名必须加引号,否则报错
    name: "{{ my_db.name }}"
    state: present
    encoding: "{{ my_db.encoding }}"
 
- name: grant all on *.* to 'bob'@'192.168.71.%' identified by '12345';
  mysql_user:
    login_unix_socket: /var/lib/mysql/mysql.sock
    # 引用变量名必须加引号,否则报错
    name: "{{ my_user.name }}"
    host: "{{ my_user.host }}"
    password: "{{ my_user.password }}"
    priv: "{{ my_user.priv }}"
    state: present

创建变量文件

[root@m01 roles]# cat mysql/vars/main.yml
my_db:
  name: wordpress
  encoding: utf8mb4

my_user:
  name: bob
  host: 192.168.71.%
  password: 12345
  priv: '*.*:ALL'

8 lb role

1) 准备文件
准备nginx.repo

cat > lb/files/nginx.repo << "EOF"
[nginx-stable]
name=nginx stable repo
baseurl=http://nginx.org/packages/centos/$releasever/$basearch/
gpgcheck=1
enabled=1
gpgkey=https://nginx.org/keys/nginx_signing.key
module_hotfixes=true

[nginx-mainline]
name=nginx mainline repo
baseurl=http://nginx.org/packages/mainline/centos/$releasever/$basearch/
gpgcheck=1
enabled=0
gpgkey=https://nginx.org/keys/nginx_signing.key
module_hotfixes=true

EOF

准备证书

openssl genrsa -out server.key 2048 
openssl req -new -x509 -days 3650 -key server.key -out server.crt -subj "/C=CH/ST=mykey/L=mykey/O=mykey/OU=mykey/CN=domain1/CN=www.egon.com/CN=domain3"

放置证书到lb/files目录下

[root@m01 roles]# mv server.* lb/files/

准备配置文件

cat > lb/files/nginx.conf << "EOF"
user nginx;
worker_processes auto;
error_log /var/log/nginx/error.log;
pid /run/nginx.pid;
include /usr/share/nginx/modules/*.conf;
events {
    worker_connections 1024;
}
http {
    upstream webserver {
      server 192.168.71.14:8181;
      server 192.168.71.15:8181;
    }
    server {
        listen 443 ssl;
        server_name www.egon.com 192.168.71.12;
        ssl_certificate /etc/nginx/ssl_key/server.crt;
        ssl_certificate_key /etc/nginx/ssl_key/server.key;

        location / {
            proxy_pass  http://webserver;
           # 把真实的访问者ip发给后端web,后端web会据此来拼接静态文件的url地址以便让访问者浏览器发起二次请求
           # 如果没有下面的这段内容,后端web会将静态资源的url地址拼成http://webserver/static/img/1.jpg的形式,导致访问者浏览器二次访问失败
           proxy_set_header X-Real-IP $remote_addr;
           proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
           proxy_set_header Host $http_host;
           proxy_next_upstream error timeout http_500 http_502 http_503 http_504 http_403 http_404;
        }
    }
    server {
        listen 80;
        server_name 192.168.71.12 www.egon.com;

        rewrite (.*) https://$server_name$1;
    }
}
EOF

文件总共这么多

[root@m01 roles]# ls lb/files/
nginx.conf  nginx.repo  server.crt  server.key

编写剧本

[root@m01 roles]# cat lb/tasks/main.yml 
#1、安装nginx,配置nginx负载均衡
- name: clear nginx
  shell: >
    yum remove nginx* -y ; rm -rf /etc/nginx /usr/share/nginx

- name: copy nginx.repo
  copy:
    src: nginx.repo
    dest: /etc/yum.repos.d/nginx.repo

- name: Install nginx
  yum:
    name: nginx
    state: latest

- name: Create dir
  file:
    path: /etc/nginx/ssl_key
    state: directory

- name: copy multiple files
  copy:
    src: "{{ item.src }}"
    dest: "{{ item.dest }}"
  with_items:
    - { src: 'nginx.conf', dest: '/etc/nginx/nginx.conf'}
    - { src: 'server.crt', dest: '/etc/nginx/ssl_key/server.crt'}
    - { src: 'server.key', dest: '/etc/nginx/ssl_key/server.key'}
  notify: restart_nginx

- name: Start nginx server
  systemd:
    name: nginx
    state: restarted
    enabled: true

配置触发器

[root@m01 roles]# cat lb/handlers/main.yml 
- name: restart_nginx
  systemd:
    name: nginx
    state: restarted

9 wordpress role

准备文件
1、安装包

wget https://wordpress.org/latest.zip
mv latest.zip  wordpress/files/

2、准备好配置文件(配置上数据库相关信息)

cat > wordpress/files/wp-config.php << "EOF"

<?php
define( 'DB_NAME', 'wordpress' );
define( 'DB_USER', 'bob' );
define( 'DB_PASSWORD', '12345' );
define( 'DB_HOST', '192.168.71.16' );
define( 'DB_CHARSET', 'utf8mb4' );
define( 'DB_COLLATE', '' );

define( 'AUTH_KEY',         'put your unique phrase here' );
define( 'SECURE_AUTH_KEY',  'put your unique phrase here' );
define( 'LOGGED_IN_KEY',    'put your unique phrase here' );
define( 'NONCE_KEY',        'put your unique phrase here' );
define( 'AUTH_SALT',        'put your unique phrase here' );
define( 'SECURE_AUTH_SALT', 'put your unique phrase here' );
define( 'LOGGED_IN_SALT',   'put your unique phrase here' );
define( 'NONCE_SALT',       'put your unique phrase here' );

$table_prefix = 'wp_';
define( 'WP_DEBUG', false );

if ( ! defined( 'ABSPATH' ) ) {
    define( 'ABSPATH', __DIR__ . '/' );
}

require_once ABSPATH . 'wp-settings.php';

EOF

编写剧本(解压到nfs共享目录里就发布给了所有的web服务)

[root@m01 roles]# cat lb/handlers/main.yml 
me: restart_nginx
  systemd:
    name: nginx
    state: restarted
[root@m01 roles]# vi wordpress/tasks/main.yml 
[root@m01 roles]# cat wordpress/tasks/main.yml 
- name: mkdir /data
  copy:
    src: "latest.zip"
    dest: "/data"

- name: install unzip
  yum:
    name: unzip
    state: present

- name: 发布
  shell: unzip /data/latest.zip -d /data

- name: 传送配置
  copy:
    src: "wp-config.php"
    dest: "/data/wordpress/wp-config.php"

10 整合为一个playbook

编写一个palybook剧本,剧本里引入各个子role,运行的时候运行这一个plabook即可

[root@m01 roles]# cat /project/roles/run.yml 
- name: 优化部分
  hosts: all
  roles:
    - base

- name: 安装nfs
  hosts: nfs_server
  roles:
    - nfs

- name: 安装web
  hosts: web_group
  roles: # 其实你可以将安装web进一步细分为nginx role与php role,留给你作业了
    - web

- name: 安装数据库
  hosts: db_server
  roles:
    - mysql

- name: 配置负载均衡和高可用
  hosts: lb_server
  roles:
    - lb
    #- keepalived  # 高可用role留给你来实现

- name: 发布wordpress项目
  hosts: nfs_server
  roles:
    - wordpress

补充:各个子role里的hosts其实不用指定,run.yml已经指定了

执行

ansible-playbook /project/roles/run.yml 

11 测试

运行出错,修复后可以直接重新执行,因为是幂等的,所以不必担心有什么 影响
而且重复执行后,因为之前一些步骤已经完成了,会快一些

本地添加解析

# C:\Windows\System32\drivers\etc
192.168.71.12 www.egon.com

访问负载均衡地址:http://192.168.71.12/wordpress

file

file

12 代码地址

本节课程代码都放到了这里:https://gitee.com/egonlin/ansible-playbook-test
目录下有一个hosts.txt内记录着与本节实验相关的两个文件内容

/etc/ansible/hosts
/etc/hosts

练习:https://egonlin.com/?p=9884

上一篇
下一篇
Copyright © 2022 Egon的技术星球 egonlin.com 版权所有 帮助IT小伙伴学到真正的技术