第六章 集中化运维工具---Ansible和SaltStack

一.Ansible

1.1.安装Ansible

安装ansible之前要准备三台虚拟机,而且确保你已经看完书上面的讲解才能知道后面为什么这么做,虚拟机信息如下表

主机名

IP地址

gzh-cs8

192.168.88.137

gzh-a1

192.168.88.138

gzh-a2

192.168.88.139

安装ansible

[root@gzh-cs8 ~]# yum install centos-release-ansible-29.noarch
[root@gzh-cs8 ~]# yum -y install ansible
[root@gzh-cs8 ~]# ansible --version
ansible 2.9.27
  config file = /etc/ansible/ansible.cfg
  configured module search path = ['/root/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
  ansible python module location = /usr/lib/python3.6/site-packages/ansible
  executable location = /usr/bin/ansible
  python version = 3.6.8 (default, Mar 19 2021, 05:13:41) [GCC 8.4.1 20200928 (Red Hat 8.4.1-1)]

1.2.配置SSH免密登录

首先在gzh-cs8上面生成一对密钥

[root@gzh-cs8 ~]# ssh-keygen -t rsa
Generating public/private rsa key pair.
Enter file in which to save the key (/root/.ssh/id_rsa): 
Created directory '/root/.ssh'.
Enter passphrase (empty for no passphrase): 
Enter same passphrase again: 
Your identification has been saved in /root/.ssh/id_rsa.
Your public key has been saved in /root/.ssh/id_rsa.pub.
The key fingerprint is:
SHA256:WY3jPArqesmLtWpILQPFQjxezWDy86niz0XvWYc6lgk root@gzh-cs8
The key's randomart image is:
+---[RSA 3072]----+
|o+ o+            |
|.o*. o     o     |
|.ooo      + .    |
|..  o .  = .     |
|. .  +. S +      |
| + .oE.. ...     |
|.ooooo..+o .     |
|o ++=..=+ .      |
| o=*+..+.        |
+----[SHA256]-----+

该命令执行完以后会在/root/.ssh下生成一对密钥,id_rsa是私钥,id_rsa.pub是公钥,我们要把公钥发送到另外的两个虚拟机上.

[root@gzh-cs8 .ssh]# ssh-copy-id root@192.168.88.138
/usr/bin/ssh-copy-id: INFO: Source of key(s) to be installed: "/root/.ssh/id_rsa.pub"
The authenticity of host '192.168.88.138 (192.168.88.138)' can't be established.
ECDSA key fingerprint is SHA256:79kcnML33BSqx2Gn4ONmU3FpV4j2JJwgM4SsFmjceP4.
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
/usr/bin/ssh-copy-id: INFO: attempting to log in with the new key(s), to filter out any that are already installed
/usr/bin/ssh-copy-id: INFO: 1 key(s) remain to be installed -- if you are prompted now it is to install the new keys
root@192.168.88.138's password: 
​
Number of key(s) added: 1
​
Now try logging into the machine, with:   "ssh 'root@192.168.88.138'"
and check to make sure that only the key(s) you wanted were added.
​
[root@gzh-cs8 .ssh]# ssh-copy-id root@192.168.88.139
/usr/bin/ssh-copy-id: INFO: Source of key(s) to be installed: "/root/.ssh/id_rsa.pub"
The authenticity of host '192.168.88.139 (192.168.88.139)' can't be established.
ECDSA key fingerprint is SHA256:79kcnML33BSqx2Gn4ONmU3FpV4j2JJwgM4SsFmjceP4.
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
/usr/bin/ssh-copy-id: INFO: attempting to log in with the new key(s), to filter out any that are already installed
/usr/bin/ssh-copy-id: INFO: 1 key(s) remain to be installed -- if you are prompted now it is to install the new keys
root@192.168.88.139's password: 
​
Number of key(s) added: 1
​
Now try logging into the machine, with:   "ssh 'root@192.168.88.139'"
and check to make sure that only the key(s) you wanted were added.

然后我们就可以实现gzh-cs8到其他两个机子的免密登录了.

1.3主机目录

我们修改/etc/ansible/hosts文件(建议跟着书一起看),清空改为如下

[root@gzh-cs8 ~]# vim /etc/ansible/hosts 
[root@gzh-cs8 ~]# cat /etc/ansible/hosts 
[/etc/Ansible/hosts]
192.168.88.138
192.168.88.139
​
​
​
[webservers]
192.168.88.138
192.168.88.139

修改完以后我们对节点进行ping操作

[root@gzh-cs8 ~]# ansible webservers -m ping
[WARNING]: Invalid characters were found in group names but not replaced, use -vvvv to see
details
192.168.88.139 | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/libexec/platform-python"
    },
    "changed": false,
    "ping": "pong"
}
192.168.88.138 | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/libexec/platform-python"
    },
    "changed": false,
    "ping": "pong"
}

1.4模块用法

  1. setup模块:查看目录节点的各种信息

    [root@gzh-cs8 ~]# ansible webservers -m setup
    [WARNING]: Invalid characters were found in group names but not replaced, use -vvvv to see
    details
    192.168.88.139 | SUCCESS => {
        "ansible_facts": {
            "ansible_all_ipv4_addresses": [
                "192.168.88.139"
            ],
            "ansible_all_ipv6_addresses": [
                "fe80::20c:29ff:fed8:8187"
            ],
            "ansible_apparmor": {
                "status": "disabled"
            },
            "ansible_architecture": "x86_64",
            "ansible_bios_date": "11/12/2020",
            "ansible_bios_version": "6.00",
            "ansible_cmdline": {
                "BOOT_IMAGE": "(hd0,msdos1)/vmlinuz-4.18.0-305.3.1.el8.x86_64",
                "crashkernel": "auto",
                "quiet": true,
                "rd.lvm.lv": "cl/swap",
                "resume": "/dev/mapper/cl-swap",
                "rhgb": true,
                "ro": true,
                "root": "/dev/mapper/cl-root"
            },
            ......

  2. copy模块:将主控端的文件复制到远程主机,只针对文件

    src  源文件路径
    dest   目标文件路径
    content  将指定内容覆盖写入到目标主机文件中
    force=no    当主控端拷贝的文件名和目标名一致,但是内容不一致,放弃拷贝
    force=yes   当主控端拷贝的文件名和目标名一致,但是内容不一致,则进行覆盖
    backup=yes   当主控端拷贝的文件名和目标名一致,但是内容不一致,则进行备份
    例:
    # 将/root/test 复制到所有节点的/root目录下
    [root@gzh-cs8 ~]# ansible all -m copy -a 'dest=/root src=/root/test'
    [WARNING]: Invalid characters were found in group names but not replaced, use -vvvv to see
    details
    192.168.88.139 | CHANGED => {
        "ansible_facts": {
            "discovered_interpreter_python": "/usr/libexec/platform-python"
        },
        "changed": true,
        "checksum": "da39a3ee5e6b4b0d3255bfef95601890afd80709",
        "dest": "/root/test",
        "gid": 0,
        "group": "root",
        "md5sum": "d41d8cd98f00b204e9800998ecf8427e",
        "mode": "0644",
        "owner": "root",
        "secontext": "system_u:object_r:admin_home_t:s0",
        "size": 0,
        "src": "/root/.ansible/tmp/ansible-tmp-1698767911.4537575-1825-7466982683333/source",
        "state": "file",
        "uid": 0
    }
    192.168.88.138 | CHANGED => {
        "ansible_facts": {
            "discovered_interpreter_python": "/usr/libexec/platform-python"
        },
        "changed": true,
        "checksum": "da39a3ee5e6b4b0d3255bfef95601890afd80709",
        "dest": "/root/test",
        "gid": 0,
        "group": "root",
        "md5sum": "d41d8cd98f00b204e9800998ecf8427e",
        "mode": "0644",
        "owner": "root",
        "secontext": "system_u:object_r:admin_home_t:s0",
        "size": 0,
        "src": "/root/.ansible/tmp/ansible-tmp-1698767911.4533894-1823-191533046233854/source",
        "state": "file",
        "uid": 0
    }
  3. file模块:创建或者和删除远程主机上的文件或者目录

    path 指定文件   如果远程主机上没有该文件,则进行创建
    state 创建类型   touch 文件  directory 目录 
    state=absent  删除文件或者目录
    ​
    link 软连接    src=源文件名  path=目标链接文件名
    hard 硬链接    src=源文件名  path=目标链接文件名
    ​
    以下三个参数,既可以修改,也可以自动添加
    mod:权限  可以在添加时设置特殊权限,前提要有执行权限( set 粘滞位)
    owner:属主
    group:属组
    例:
    # 删除刚才复制的文件
    [root@gzh-cs8 ~]# ansible all -m file -a 'path=/root/test state=absent'
    [WARNING]: Invalid characters were found in group names but not replaced, use -vvvv to see
    details
    192.168.88.138 | CHANGED => {
        "ansible_facts": {
            "discovered_interpreter_python": "/usr/libexec/platform-python"
        },
        "changed": true,
        "path": "/root/test",
        "state": "absent"
    }
    192.168.88.139 | CHANGED => {
        "ansible_facts": {
            "discovered_interpreter_python": "/usr/libexec/platform-python"
        },
        "changed": true,
        "path": "/root/test",
        "state": "absent"
    }
  4. command模块shell模块

    command模块在远程主机上执行指定得命令 如:cat ls ,不能使用特殊得符号 :| > >>

    语法:
    ansible  主机清单 -m  模块名 -a  '执行命令'
    creates:当指定文件存在时,后一条命令不执行 / 指定文件不存在,后一条命令执行
    removes:当指定文件存在时,后一条命令执行 / 指定文件不存在,后一条命令不执行
    # 查看所有节点/root下的文件
    [root@gzh-cs8 ~]# ansible all  -m command  -a 'ls /root'
    [WARNING]: Invalid characters were found in group names but not replaced, use -vvvv to see
    details
    192.168.88.138 | CHANGED | rc=0 >>
    anaconda-ks.cfg
    linux-5.10.10
    linux-5.10.10.tar.gz
    yum.sh
    192.168.88.139 | CHANGED | rc=0 >>
    anaconda-ks.cfg
    linux-5.10.10
    linux-5.10.10.tar.gz
    yum.sh

    shell模块在远程主机上执行复杂的命令,比较好用得模块

    语法:
    ansible 主机清单 -m  模块名 -a  '执行命令'
    # 查看所有节点/root下的文件
    [root@gzh-cs8 ~]# ansible all  -m shell  -a 'ls /root'
    [WARNING]: Invalid characters were found in group names but not replaced, use -vvvv to see
    details
    192.168.88.139 | CHANGED | rc=0 >>
    anaconda-ks.cfg
    linux-5.10.10
    linux-5.10.10.tar.gz
    yum.sh
    192.168.88.138 | CHANGED | rc=0 >>
    anaconda-ks.cfg
    linux-5.10.10
    linux-5.10.10.tar.gz
    yum.sh

还有很多模块这里就不在写了,大家下去做一下.

最后推荐大家看一下Ansible的中文文档Ansible中文官方文档 .

1.5编写playbook脚本安装Apache并启动

[root@gzh-cs8 playbook]# vim apache.yml 
[root@gzh-cs8 playbook]# cat apache.yml 
---
- name: 下载yum源
  hosts: 192.168.88.138
  tasks:
    - name: 传输yum
      copy:
        src: /etc/yum.repos.d/Centos-8.repo
        dest: /etc/yum.repos.d/Centos-8.repo


- name: 修改yum源的配置文件
  hosts: 192.168.88.138
  tasks:
    - name: 修改
      command: sed -i 's/8/7/g' /etc/yum.repos.d/Centos-8.repo


- name: 安装httpd服务
  hosts: 192.168.88.138
  tasks:
    - name: 安装
      yum:
        name: httpd
        state: present


- name: 修改httpd的配置文件
  hosts: 192.168.88.138
  tasks:
    - name: 修改
      command: sed -i 's/#ServerName www.example.com:80/ServerName www.example.com:80/' /etc/httpd/conf/httpd.conf 

- name: 启动httpd服务
  hosts: 192.168.88.138
  tasks:
    - name: 启动
      service:
        name: httpd
        state: started
        enabled: yes
    - name: 关闭
      service:
        name: firewalld
        state: stopped
        enabled: no
    - name: 重启
      service:
        name: httpd
        state: restarted
[root@gzh-cs8 playbook]# ansible-playbook /etc/ansible/playbook/apache.yml 
[WARNING]: Invalid characters were found in group names but not replaced, use -vvvv to see
details

PLAY [下载yum源] ******************************************************************************

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

TASK [传输yum] *******************************************************************************
ok: [192.168.88.138]

PLAY [修改yum源的配置文件] *************************************************************************

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

TASK [修改] **********************************************************************************
[WARNING]: Consider using the replace, lineinfile or template module rather than running
'sed'.  If you need to use command because replace, lineinfile or template is insufficient
you can add 'warn: false' to this command task or set 'command_warnings=False' in
ansible.cfg to get rid of this message.
changed: [192.168.88.138]

PLAY [安装httpd服务] ***************************************************************************

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

TASK [安装] **********************************************************************************
ok: [192.168.88.138]

PLAY [修改httpd的配置文件] ************************************************************************

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

TASK [修改] **********************************************************************************
changed: [192.168.88.138]

PLAY [启动httpd服务] ***************************************************************************

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

TASK [启动] **********************************************************************************
changed: [192.168.88.138]

TASK [关闭] **********************************************************************************
changed: [192.168.88.138]

TASK [重启] **********************************************************************************
changed: [192.168.88.138]

PLAY RECAP *********************************************************************************
192.168.88.138             : ok=12   changed=5    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0  

1.6编写部署nginx脚本

在编写脚本之前先了解nginx目录结构,和个目录存放的文件,这里参考书上的

Nginx 是一个高性能的开源 Web 服务器和反向代理服务器,通过使用 Ansible,可以实现自动化部署和配置 Nginx,简化服务器的设置和管理,并提高系统的可靠性和性能。

[root@gzh-cs8 ~]# mkdir /etc/ansible/roles/nginx
[root@gzh-cs8 ~]# cd /etc/ansible/roles/nginx/
[root@gzh-cs8 nginx]# ls
[root@gzh-cs8 nginx]# mkdir tasks templates handlers files vars
[root@gzh-cs8 nginx]# ls
files  handlers  tasks  templates
[root@gzh-cs8 nginx]# cd tasks
[root@gzh-cs8 tasks]# vim main.yml
[root@gzh-cs8 tasks]# cat main.yml 
---
  - name: intall epel
    yum: name=epel-release state=latest
  - name: install nginx
    yum: name=nginx state=latest
  - name: copy nginx.conf  templte
    template: src=nginx.conf.j2 dest=/etc/nginx/nginx.conf
  - name: copy index.html
    copy: src=/etc/ansible/roles/nginx/files/index.html dest=/usr/share/nginx/html/index.html
    notify: start nginx
[root@gzh-cs8 files]# vim index.html   
[root@gzh-cs8 files]# cat index.html 
hello Ansible-nginx
[root@gzh-cs8 handlers]# vim main.yml
[root@gzh-cs8 handlers]# cat main.yml 
---
- name: start nginx  #和notify的名字必须一样
  service: name=nginx state=started
[root@gzh-cs8 handlers]# cd ../vars
[root@gzh-cs8 vars]# vim main.yml
[root@gzh-cs8 vars]# cat main.yml 
worker_connections: 2
[root@gzh-cs8 vars]# cd ../templates/
[root@gzh-cs8 templates]# vim nginx.conf.j2 
[root@gzh-cs8 templates]# cat nginx.conf.j2 
worker_processes  2;
http {
      include mime.types;
      default_type application/octet-stream;
      keepalive_timeout 65;
      server {
           listen 80;
            server_name localhost;
            location / {
              root html;
             index index.html index.htm;
            }
      }
}
 events {
            worker_connections  1024;    #nginx的最大并发访问量
            use epoll;    #异步IO
        }
[root@gzh-cs8 nginx]# cd ../
[root@gzh-cs8 roles]# vim site.yml
[root@gzh-cs8 roles]# cat 
nginx/    site.yml  
[root@gzh-cs8 roles]# cat site.yml 
---
- hosts: 192.168.88.138
  roles:
       - nginx
[root@gzh-cs8 roles]# ansible-playbook site.yml 
[WARNING]: Invalid characters were found in group names but not replaced, use -vvvv to see
details

PLAY [192.168.88.138] **********************************************************************

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

TASK [nginx : intall epel] *****************************************************************
changed: [192.168.88.138]

TASK [install nginx] ***********************************************************************
ok: [192.168.88.138]

TASK [copy nginx.conf  templte] ************************************************************
ok: [192.168.88.138]

TASK [nginx : copy index.html] *************************************************************
ok: [192.168.88.138]

PLAY RECAP *********************************************************************************
192.168.88.138             : ok=5    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

打开浏览器验证

image-20231102150759614

二.SaltStack

2.1SaltStack的安装与配置

主机信息:

主机名

IP地址

gzh-cs8

192.168.88.137

gzh-a1

192.168.88.138

gzh-a2

192.168.88.139

1.Master端安装(gzh-cs8)

yum -y install https://repo.saltstack.com/py3/redhat/salt-py3-repo-latest.el8.noarch.rpm
yum -y install salt-master

2.Minion端安装(gzh-a1,gzh-a2)

yum -y install https://repo.saltstack.com/py3/redhat/salt-py3-repo-latest.el8.noarch.rpm
yum -y install salt-minion

修改/etc/salt/minion文件

master: 192.168.88.137
id: saltminion1

3.链接测试

master端

[root@gzh-cs8 roles]# systemctl start salt-master

minion端

[root@gzh-a1 ~]# systemctl start salt-minion
[root@gzh-a2 ~]# systemctl start salt-minion

在master端测试

[root@gzh-cs8 ~]# salt-key
Accepted Keys:
Denied Keys:
Unaccepted Keys:
saltminion1
saltminion2
Rejected Keys:

如果没出现上面的内容关闭防火墙再试一下

链接

[root@gzh-cs8 ~]# salt-key -a saltminion1,saltminion2
The following keys are going to be accepted:
Unaccepted Keys:
saltminion1
saltminion2
Proceed? [n/Y] Y
Key for minion saltminion1 accepted.
Key for minion saltminion2 accepted.
[root@gzh-cs8 ~]# salt-key
Accepted Keys:
saltminion1
saltminion2
Denied Keys:
Unaccepted Keys:
Rejected Keys:

下面的模板用法就不写了,跟着书看一看和Ansible的用法差不多,我们直接进入实例

2.2部署LAMP环境

LAMP 为 Linux、Apache、MySQL、PHP 的简称,这是一个常规的 Web 服务器环境解决方案,使用其首字母缩写“LAMP”来引用。它是一个用于创建和管理 Web 应用程序的开源开发平台。Linux 用作后端操作系统(OS)。Apache 是Web 服务器,MySQL 是数据库,PHP 是脚本语言。

搭建LAMP环境需要编写三个模块: httpd,php,mysql

/srv/salt目录下面创建state文件lamp.sls用来实现各部分的功能

[root@gzh-cs8 salt]# vim lamp.sls 
[root@gzh-cs8 salt]# cat lamp.sls 
###httpd模块###
install_httpd:
  pkg.installed:
    - name: httpd
http_running:
  service.running:
    - name: httpd
    - enable: Ture
    - require:
        - pkg: install_httpd
    - watch:
        - file: httpd_conf
        - file: php_conf
httpd_conf:
  file.managed:
    - name: /etc/httpd/conf/httpd.conf
    - source:
        salt: //httpd.conf
    - user: root
    - group: root
    - mode: 600
###php模块###
install_php:
  pkg.installed:
    - name: php_all
    - pkgs:
        - php
        - php-mysql
        - php-common
        - php-gd
        - php-mbstring
        - php-devel
        - php-xml
    - require:
        - pkg: install_httpd
php_conf:
  file.managed:
    - name: /etc/php.ini
    - source:
        salt: //php.ini
    - user: root
    - group: root
    - mode: 600
###mysql模块###
mysql_repo_install:
  cmd.run:
    - onlyif: [ ! -f /etc/yum.repos.d/mysql-community.repo ]
    - names:
        - rpm -ivh http://repo.mysql.com/mysql-community-release-sl7-5.noarch.rpm
install_mysql:
  pkg.installed:
    - name: mysql-community-server
    - pkgs:
        - mysql-community-client
        - mysql-community-devel
mysql_running:
  service.running:
    - name: mysql

这是书上的脚本,他把三个模块写在一起了,那你这里面运行肯定是报错的,因为里面的httpd.confphp.ini配置文件都是不存在的,因为在实际的运维中,不同服务的配置文件是不一样的,所以,这里面的配置文件的操作实际上是将写好的配置文件上传到saltminion1节点上.因为不存在所以就会报错.

[root@gzh-cs8 ~]# cd /srv/salt/
[root@gzh-cs8 salt]# ls
httpd  mysql  php  top.sls
[root@gzh-cs8 salt]# tree .
.
├── httpd
│   ├── httpd.sls
│   └── index.php
├── mysql
│   ├── my.sh
│   └── mysql.sls
├── php
│   └── php.sls
└── top.sls

3 directories, 6 files
[root@gzh-cs8 salt]# cat httpd/httpd.sls 
#安装httpd
httpd-install:
  pkg.installed:
    - name: httpd
#传输本地主页文件到远端
httpd-index:
  file.managed:
    - name: /var/www/html/index.php
    - source: salt://httpd/index.php
#开启服务
httpd-service:
  service.running:
    - name: httpd
    - enable: True
[root@gzh-cs8 salt]# cat httpd/index.php 
<?php
phpinfo();
?>
[root@gzh-cs8 salt]# cat mysql/my.sh 
#设置密码
mysqladmin password "guo123"
#创建数据库
mysql -u root --password="guo123" -e "create database if not exists mydb"
[root@gzh-cs8 salt]# cat mysql/mysql.sls 
mysql-install:
  pkg.installed:
    - pkgs:
      - mariadb-server
      - mariadb

mysql-service:
  service.running:
    - name: mariadb
    - enable: True
#传送脚本并执行脚本
mysql-script:
  file.managed:
    - name: /tmp/my.sh
    - source: salt://mysql/my.sh
  cmd.run:
    - name: cd /tmp/ && chmod +x my.sh && ./my.sh
[root@gzh-cs8 salt]# cat php/php.sls 
php-install:
  pkg.installed:
    - pkgs:
      - php
      - php-gd
      - php-ldap
      - php-odbc
      - php-pear
      - php-xml
      - php-xmlrpc
      - php-mbstring
      - php-snmp
      - php-soap
      - curl
      - libcurl-devel
      - php-bcmath
#重启httpd服务
httpd-stop:
  cmd.run:
    - name: systemctl restart httpd
[root@gzh-cs8 salt]# cat top.sls 
base:
  'saltminion1':
    - httpd.httpd
    - mysql.mysql
    - php.php
[root@master ~]# salt 'saltminion1' state.highstate
#执行成功显示如下
Succeeded: 9 
Failed:    0