2020-01-17
此为ansible批量管理服务的第1个博客,发布博客为ansible批量管理服务入门
综合架构(二)——ansible批量管理服务
一ansible批量管理服务功能
01.可以实现批量系统操作配置
02.可以实现批量软件服务部署
03.可以实现批量文件数据分发
04.可以实现批量系统信息收集,资产管理
二ansible批量管理服务特点
01.管理端不需要启动服务程序(no server)
02.管理端不需要编写配置文件(/etc/ansible/ansible.cfg)
03.受控端不需要安装软件程序(libselinux-python)
04.受控端不需要启动服务程序(no agent)
05.服务程序管理操作模块众多(module)
06.利用剧本编写来实现自动化(playbook)
三批量管理软件安装部暑过程
[root@manager ~]# yum install -y ansible
Loaded plugins: fastestmirror
Loading mirror speeds from cached hostfile
* base: mirrors.aliyun.com
* extras: mirrors.aliyun.com * updates: mirrors.aliyun.com base | 3.6 kB 00:00 epel | 5.4 kB 00:00 extras | 2.9 kB 00:00 updates | 2.9 kB 00:00 (1/2): epel/x86_64/updateinfo | 1.0 MB 00:01 (2/2): epel/x86_64/primary_db | 6.9 MB 00:02 Package ansible-2.9.2-1.el7.noarch already installed and latest version Nothing to do
涉及到的组件非常多
[root@manager ~]# rpm -ql ansible | wc
17280 17280 1446145 [root@manager ~]# rpm -ql ansible | head /etc/ansible /etc/ansible/ansible.cfg /etc/ansible/hosts /etc/ansible/roles /usr/bin/ansible /usr/bin/ansible-2 /usr/bin/ansible-2.7 /usr/bin/ansible-config /usr/bin/ansible-connection /usr/bin/ansible-console [root@manager ~]# rpm -ql ansible | tail /usr/share/doc/ansible-2.9.2/README.rst /usr/share/man/man1/ansible-config.1.gz /usr/share/man/man1/ansible-console.1.gz /usr/share/man/man1/ansible-doc.1.gz /usr/share/man/man1/ansible-galaxy.1.gz /usr/share/man/man1/ansible-inventory.1.gz /usr/share/man/man1/ansible-playbook.1.gz /usr/share/man/man1/ansible-pull.1.gz /usr/share/man/man1/ansible-vault.1.gz /usr/share/man/man1/ansible.1.gz [root@manager ~]#
查看ansible的版本
版本很重要,因为有些服务的版本可能隔一段时间就会发生变化,相关的功能也会变化
[root@manager ~]# ansible --version
ansible 2.9.2
config file = /etc/ansible/ansible.cfg configured module search path = [u'/root/.ansible/plugins/modules', u'/usr/share/ansible/plugins/modules'] ansible python module location = /usr/lib/python2.7/site-packages/ansible executable location = /usr/bin/ansible python version = 2.7.5 (default, Oct 30 2018, 23:45:53) [GCC 4.8.5 20150623 (Red Hat 4.8.5-36)]
注意只能使用长格式查看
[root@manager ~]# ansible -V
usage: ansible [-h] [--version] [-v] [-b] [--become-method BECOME_METHOD]
[--become-user BECOME_USER] [-K] [-i INVENTORY] [--list-hosts]
[-l SUBSET] [-P POLL_INTERVAL] [-B SECONDS] [-o] [-t TREE] [-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] [-C] [--syntax-check] [-D] [-e EXTRA_VARS] [--vault-id VAULT_IDS] [--ask-vault-pass | --vault-password-file VAULT_PASSWORD_FILES] [-f FORKS] [-M MODULE_PATH] [--playbook-dir BASEDIR] [-a MODULE_ARGS] [-m MODULE_NAME] pattern ansible: error: too few arguments [root@manager ~]# ansible -v usage: ansible [-h] [--version] [-v] [-b] [--become-method BECOME_METHOD] [--become-user BECOME_USER] [-K] [-i INVENTORY] [--list-hosts] [-l SUBSET] [-P POLL_INTERVAL] [-B SECONDS] [-o] [-t TREE] [-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] [-C] [--syntax-check] [-D] [-e EXTRA_VARS] [--vault-id VAULT_IDS] [--ask-vault-pass | --vault-password-file VAULT_PASSWORD_FILES] [-f FORKS] [-M MODULE_PATH] [--playbook-dir BASEDIR] [-a MODULE_ARGS] [-m MODULE_NAME] pattern ansible: error: too few arguments
ansible软件学习说明:
1) 掌握ansible主机清单配置: 指定ansible程序可以批量管理哪些主机
https://docs.ansible.com/ansible/latest/user_guide/intro_inventory.html
2) 掌握ansible常用功能模块: 实现批量管理功能模块
https://docs.ansible.com/ansible/latest/modules/modules_by_category.html
3) 掌握ansible剧本编写方法: 实现自动化批量管理功能
https://docs.ansible.com/ansible/latest/user_guide/playbooks.html
四网站主机清单配置
(一)方式一:直接在配置文件/etc/ansible/hosts写入主机地址
[root@manager ~]# tail /etc/ansible/hosts
# Here's another example of host ranges, this time there are no
# leading 0s: ## db-[99:101]-node.example.com 172.16.1.7 172.16.1.8 172.16.1.31 172.16.1.41
m表示module
[root@manager ~]# ansible --help | grep "\-m " [-a MODULE_ARGS] [-m MODULE_NAME] -m MODULE_NAME, --module-name MODULE_NAME
在命令行检测
ping 模块:测试主机之间的连通性
参考:https://docs.ansible.com/ansible/latest/modules/ping_module.html#ping-module
[root@manager ~]# ansible all -m ping
172.16.1.41 | SUCCESS => {
"ansible_facts": { "discovered_interpreter_python": "/usr/bin/python" }, "changed": false, "ping": "pong" } 172.16.1.7 | SUCCESS => { "ansible_facts": { "discovered_interpreter_python": "/usr/bin/python" }, "changed": false, "ping": "pong" } 172.16.1.31 | SUCCESS => { "ansible_facts": { "discovered_interpreter_python": "/usr/bin/python" }, "changed": false, "ping": "pong" } 172.16.1.8 | SUCCESS => { "ansible_facts": { "discovered_interpreter_python": "/usr/bin/python" }, "changed": false, "ping": "pong" }
[root@manager ~]# ansible 172.16.1.8 -m ping
172.16.1.8 | SUCCESS => { "ansible_facts": { "discovered_interpreter_python": "/usr/bin/python" }, "changed": false, "ping": "pong" } [root@manager ~]# ansible 172.16.1.7 -m ping 172.16.1.7 | SUCCESS => { "ansible_facts": { "discovered_interpreter_python": "/usr/bin/python" }, "changed": false, "ping": "pong" } [root@manager ~]# ansible 172.16.1.31 -m ping 172.16.1.31 | SUCCESS => { "ansible_facts": { "discovered_interpreter_python": "/usr/bin/python" }, "changed": false, "ping": "pong" } [root@manager ~]# ansible 172.16.1.41 -m ping 172.16.1.41 | SUCCESS => { "ansible_facts": { "discovered_interpreter_python": "/usr/bin/python" }, "changed": false, "ping": "pong" } [root@manager ~]#
注意命令的顺序不能写错的
[root@manager ~]# ansible -m 172.16.1.7 ping
[WARNING]: Could not match supplied host pattern, ignoring: ping [WARNING]: No hosts matched, nothing to do [root@manager ~]# ansible -m 172.16.1.8 ping [WARNING]: Could not match supplied host pattern, ignoring: ping [WARNING]: No hosts matched, nothing to do [root@manager ~]#
(二)方式二:基于密码方式进行配置
参考官网,比较麻烦
把公钥删除,这是在受控端没有管理端的公钥的情况下配置的
以备份服务器为受控端
[root@nfs01 ~]# ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000 link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 inet 127.0.0.1/8 scope host lo valid_lft forever preferred_lft forever inet6 ::1/128 scope host valid_lft forever preferred_lft forever 2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000 link/ether 00:0c:29:b9:d6:60 brd ff:ff:ff:ff:ff:ff inet 10.0.0.31/24 brd 10.0.0.255 scope global noprefixroute eth0 valid_lft forever preferred_lft forever inet6 fe80::d2e4:6879:ac85:c625/64 scope link tentative noprefixroute dadfailed valid_lft forever preferred_lft forever inet6 fe80::8c8d:c31b:a121:ca62/64 scope link tentative noprefixroute dadfailed valid_lft forever preferred_lft forever inet6 fe80::9d5f:63cb:4c46:8ce7/64 scope link noprefixroute valid_lft forever preferred_lft forever 3: eth1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000 link/ether 00:0c:29:b9:d6:6a brd ff:ff:ff:ff:ff:ff inet 172.16.1.31/24 brd 172.16.1.255 scope global noprefixroute eth1 valid_lft forever preferred_lft forever inet6 fe80::e654:55e2:8c88:ecfb/64 scope link tentative noprefixroute dadfailed valid_lft forever preferred_lft forever inet6 fe80::5e65:529b:4fae:7fc3/64 scope link tentative noprefixroute dadfailed valid_lft forever preferred_lft forever inet6 fe80::787b:a3a1:ead:b12e/64 scope link noprefixroute valid_lft forever preferred_lft forever
[root@nfs01 ~]# ls /root/.ssh/
authorized_keys
[root@nfs01 ~]# ll /root/.ssh/
-bash: ll: command not found [root@nfs01 ~]# ls -l /root/.ssh/ total 4 -rw------- 1 root root 394 Jan 16 22:47 authorized_keys [root@nfs01 ~]# cat /root/.ssh/authorized_keys ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDhE06kiM6UKlnZqGPBahZuAgq+LHdSssZrInWpkecC5buh2RRzl2NOiXCWNhEIwNgIJjLGsnITrTOyRsOel94oUYHR4iSPPhAe8yVuTZA8fS9ti1goIKt6/UYhi6bbNmFiqWPlSq1EIgpNvlWaDjHIchdIHR/lbgP3Wjg2PPI2QtzR5/SZJOI0g/GTJw1FDGXpYUlz608YTo79tl15zUAGT5rLiu5ugXY28HGQbPru5xA+BQZyzKu4jInllM8WzLimpFb7WX8HagwSU2S/Hx2+HSzEbMHXqz0tm8WbQogEO78gv0cJ6USq8vyc1ueq/Jn27gDu3ygPXtcSCn0XKpZJ root@manager
[root@manager ~]# tail -1 /etc/ansible/hosts
172.16.1.31 [root@manager ~]# ansible 172.16.1.31 -m ping 172.16.1.31 | SUCCESS => { "ansible_facts": { "discovered_interpreter_python": "/usr/bin/python" }, "changed": false, "ping": "pong" }
删除受控端的管理端对应的公钥
[root@nfs01 ~]# rm -f /root/.ssh/authorized_keys
[root@nfs01 ~]# ls /root/.ssh/authorized_keys ls: cannot access /root/.ssh/authorized_keys: No such file or directory
此时管理端要输入密码才可以登录到受控端
[root@manager ~]# ssh 172.16.1.31
root@172.16.1.31's password: Last login: Sun Jan 26 17:30:17 2020 from 172.16.1.61 [root@nfs01 ~]# exit logout Connection to 172.16.1.31 closed.
修改配置文件并且进行
[root@manager ~]# tail -1 /etc/ansible/hosts
172.16.1.31 ansible_user=root ansible_password=123456 ansible_port=22 [root@manager ~]# ansible 172.16.1.31 -m ping 172.16.1.31 | SUCCESS => { "ansible_facts": { "discovered_interpreter_python": "/usr/bin/python" }, "changed": false, "ping": "pong" }
(三)方式三: 采用主机组方式配置
分组进行操作,和班级进行分组管理一样,管理起来更加方便
[root@manager ~]# tail /etc/ansible/hosts
## db-[99:101]-node.example.com [web] 172.16.1.7 172.16.1.8 [nfs] 172.16.1.31 [backup] 172.16.1.41
执行命令
[root@manager ~]# ansible web -m ping
172.16.1.7 | SUCCESS => {
"ansible_facts": { "discovered_interpreter_python": "/usr/bin/python" }, "changed": false, "ping": "pong" } 172.16.1.8 | SUCCESS => { "ansible_facts": { "discovered_interpreter_python": "/usr/bin/python" }, "changed": false, "ping": "pong" } [root@manager ~]# ansible nfs -m ping 172.16.1.31 | SUCCESS => { "ansible_facts": { "discovered_interpreter_python": "/usr/bin/python" }, "changed": false, "ping": "pong" } [root@manager ~]# ansible backup -m ping 172.16.1.41 | SUCCESS => { "ansible_facts": { "discovered_interpreter_python": "/usr/bin/python" }, "changed": false, "ping": "pong" }
主机子组配置,对组进行管理
[root@manager ~]# tail -15 /etc/ansible/hosts
# leading 0s:
## db-[99:101]-node.example.com [client:children] web nfs backup [web] 172.16.1.7 172.16.1.8 [nfs] 172.16.1.31 [backup] 172.16.1.41
执行命令
[root@manager ~]# ansible client -m ping
172.16.1.31 | SUCCESS => {
"ansible_facts": { "discovered_interpreter_python": "/usr/bin/python" }, "changed": false, "ping": "pong" } 172.16.1.7 | SUCCESS => { "ansible_facts": { "discovered_interpreter_python": "/usr/bin/python" }, "changed": false, "ping": "pong" } 172.16.1.8 | SUCCESS => { "ansible_facts": { "discovered_interpreter_python": "/usr/bin/python" }, "changed": false, "ping": "pong" } 172.16.1.41 | SUCCESS => { "ansible_facts": { "discovered_interpreter_python": "/usr/bin/python" }, "changed": false, "ping": "pong" }
(四)方式四:采用匹配方式配置主机信息
[7:20]表示IP地址最后一位为7到20的主机
[root@manager ~]# tail -1 /etc/ansible/hosts
172.16.1.[7:20]
执行命令
31,41没有匹配
[root@manager ~]# ansible 172.16.1.7 -m ping
172.16.1.7 | SUCCESS => { "ansible_facts": { "discovered_interpreter_python": "/usr/bin/python" }, "changed": false, "ping": "pong" } [root@manager ~]# ansible 172.16.1.8 -m ping 172.16.1.8 | SUCCESS => { "ansible_facts": { "discovered_interpreter_python": "/usr/bin/python" }, "changed": false, "ping": "pong" } [root@manager ~]# ansible 172.16.1.31 -m ping [WARNING]: Could not match supplied host pattern, ignoring: 172.16.1.31 [WARNING]: No hosts matched, nothing to do [root@manager ~]# ansible 172.16.1.41 -m ping [WARNING]: Could not match supplied host pattern, ignoring: 172.16.1.41 [WARNING]: No hosts matched, nothing to do
[root@manager ~]# tail -1 /etc/ansible/hosts
172.16.1.[1:20] [root@manager ~]# ansible 172.16.1.7 -m ping 172.16.1.7 | SUCCESS => { "ansible_facts": { "discovered_interpreter_python": "/usr/bin/python" }, "changed": false, "ping": "pong" } [root@manager ~]# ansible 172.16.1.8 -m ping 172.16.1.8 | SUCCESS => { "ansible_facts": { "discovered_interpreter_python": "/usr/bin/python" }, "changed": false, "ping": "pong" } [root@manager ~]# ansible 172.16.1.31 -m ping [WARNING]: Could not match supplied host pattern, ignoring: 172.16.1.31 [WARNING]: No hosts matched, nothing to do [root@manager ~]# ansible 172.16.1.41 -m ping [WARNING]: Could not match supplied host pattern, ignoring: 172.16.1.41 [WARNING]: No hosts matched, nothing to do
五批量管理服务应用方法
批量管理命令语法结构:
ansible 管理主机信息/主机组信息 -m 模块名称 -a "模块参数"
[root@manager ~]# tail /etc/ansible/hosts
# Here's another example of host ranges, this time there are no
# leading 0s: ## db-[99:101]-node.example.com [client] 172.16.1.7 172.16.1.8 172.16.1.31 172.16.1.41
(一)command模块 (默认)
[root@manager ~]# ansible client -a "free -h"
172.16.1.7 | CHANGED | rc=0 >> total used free shared buff/cache available Mem: 972M 138M 591M 7.7M 242M 651M Swap: 1.5G 0B 1.5G 172.16.1.31 | CHANGED | rc=0 >> total used free shared buff/cache available Mem: 972M 140M 642M 7.7M 189M 647M Swap: 1.5G 0B 1.5G 172.16.1.41 | CHANGED | rc=0 >> total used free shared buff/cache available Mem: 972M 136M 673M 7.7M 162M 665M Swap: 1.5G 0B 1.5G 172.16.1.8 | CHANGED | rc=0 >> total used free shared buff/cache available Mem: 972M 140M 662M 7.7M 169M 657M Swap: 1.5G 0B 1.5G
[root@manager ~]# ansible client -m shell -a "rm -rf /tmp/*"
[WARNING]: Consider using the file module with state=absent rather than running 'rm'. If you need to use command because file 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. 172.16.1.7 | CHANGED | rc=0 >> 172.16.1.8 | CHANGED | rc=0 >> 172.16.1.31 | CHANGED | rc=0 >> 172.16.1.41 | CHANGED | rc=0 >> [root@manager ~]# ansible client -m shell -a "ls /tmp/*" 172.16.1.8 | CHANGED | rc=0 >> ansible_command_payload.zip 172.16.1.7 | CHANGED | rc=0 >> ansible_command_payload.zip 172.16.1.41 | CHANGED | rc=0 >> ansible_command_payload.zip 172.16.1.31 | CHANGED | rc=0 >> ansible_command_payload.zip
[root@manager ~]# ansible client -m command -a "pwd"
172.16.1.41 | CHANGED | rc=0 >> /root 172.16.1.7 | CHANGED | rc=0 >> /root 172.16.1.8 | CHANGED | rc=0 >> /root 172.16.1.31 | CHANGED | rc=0 >> /root
模块参数:chdir: 在执行批量管理命令时, 先进行目录切换
[root@manager ~]# ansible client -m command -a "chdir=/tmp pwd"
172.16.1.41 | CHANGED | rc=0 >> /tmp 172.16.1.7 | CHANGED | rc=0 >> /tmp 172.16.1.31 | CHANGED | rc=0 >> /tmp 172.16.1.8 | CHANGED | rc=0 >> /tmp
creates: 判断指定文件是否存在, 如果不存在,就执行后面命令;存在就不执行后面命令
和判断语句类似
[root@manager ~]# ansible client -m command -a "creates=/tmp/happy.txt touch /tmp/happy.txt"
[WARNING]: Consider using the file module with state=touch rather than running 'touch'. If you need to use command because file 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. 172.16.1.31 | CHANGED | rc=0 >> 172.16.1.7 | CHANGED | rc=0 >> 172.16.1.41 | CHANGED | rc=0 >> 172.16.1.8 | CHANGED | rc=0 >>
[root@manager ~]# ansible client -m command -a "chdir=/tmp ls"
172.16.1.41 | CHANGED | rc=0 >> ansible_command_payload_dKZhze happy.txt 172.16.1.8 | CHANGED | rc=0 >> ansible_command_payload_0Y2P6b happy.txt 172.16.1.7 | CHANGED | rc=0 >> ansible_command_payload_sagI1X happy.txt 172.16.1.31 | CHANGED | rc=0 >> ansible_command_payload_1XAqCp happy.txt
再次执行会跳过了
[root@manager ~]# ansible client -m command -a "creates=/tmp/happy.txt touch /tmp/happy.txt"
172.16.1.31 | SUCCESS | rc=0 >> skipped, since /tmp/happy.txt exists 172.16.1.8 | SUCCESS | rc=0 >> skipped, since /tmp/happy.txt exists 172.16.1.7 | SUCCESS | rc=0 >> skipped, since /tmp/happy.txt exists 172.16.1.41 | SUCCESS | rc=0 >> skipped, since /tmp/happy.txt exists
批量执行脚本
第一个步骤:编写脚本
第二个步骤:将脚本推送到被管理主机上
第三个步骤:将脚本文件权限进行设置
第四个步骤:批量执行脚本
https://docs.ansible.com/ansible/latest/modules/shell_module.html#shell-module
(二)shell模块(万能模块)
作用说明:批量执行命令,可以识别特殊符号
[root@manager ~]# ansible-doc --help | grep "\-s" [-j] [-F | -l | -s | --metadata-dump] prepend colon-separated path(s) to module library (def -s, --snippet Show playbook snippet for specified plugin(s)
[root@manager ~]# ansible-doc -s shell
- name: Execute shell commands on targets
shell:
chdir: # Change into this directory before running the
command.
cmd: # The command to run followed by optional
arguments.
creates: # A filename, when it already exists, this step
will *not* be
run.
executable: # Change the shell used to execute the command.
This expects an
absolute path to
the executable.
free_form: # The shell module takes a free form command to run, as a string. There is no actual parameter named 'free form'. See the examples on how to use this module. removes: # A filename, when it does not exist, this step will *not* be run. stdin: # Set the stdin of the command directly to the specified value. stdin_add_newline: # Whether to append a newline to stdin data. warn: # Whether to enable task warnings. (END)
把管理端的脚本传输到受控端的目录里面
先查看目录是否存在
[root@manager ~]# ansible client -m shell -a "ls -d /shell_scripts"
172.16.1.8 | CHANGED | rc=0 >> /shell_scripts 172.16.1.7 | CHANGED | rc=0 >> /shell_scripts 172.16.1.31 | CHANGED | rc=0 >> /shell_scripts 172.16.1.41 | CHANGED | rc=0 >> /shell_scripts
正式进行文件传输
[root@manager ~]# time ansible client -m copy -a 'src=/shell_scripts/install.sh dest=/shell_scripts'
172.16.1.41 | SUCCESS => { "ansible_facts": { "discovered_interpreter_python": "/usr/bin/python" }, "changed": false, "checksum": "d8930d18657635c362456d4e25091679006031e8", "dest": "/shell_scripts/install.sh", "gid": 0, "group": "root", "mode": "0755", "owner": "root", "path": "/shell_scripts/install.sh", "size": 36, "state": "file", "uid": 0 } 172.16.1.7 | SUCCESS => { "ansible_facts": { "discovered_interpreter_python": "/usr/bin/python" }, "changed": false, "checksum": "d8930d18657635c362456d4e25091679006031e8", "dest": "/shell_scripts/install.sh", "gid": 0, "group": "root", "mode": "0644", "owner": "root", "path": "/shell_scripts/install.sh", "size": 36, "state": "file", "uid": 0 } 172.16.1.31 | SUCCESS => { "ansible_facts": { "discovered_interpreter_python": "/usr/bin/python" }, "changed": false, "checksum": "d8930d18657635c362456d4e25091679006031e8", "dest": "/shell_scripts/install.sh", "gid": 0, "group": "root", "mode": "0755", "owner": "root", "path": "/shell_scripts/install.sh", "size": 36, "state":