1 Ansible 安装和入门
1.1 Ansible安装
- 官方EPEL源提供Ansible
# CentOS7安装
[12:02:55 root@ansible ~]#yum -y install ansible
[12:03:01 root@ansible ~]#ansible --version
ansible 2.9.16
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, Apr 2 2020, 13:16:51) [GCC 4.8.5 20150623 (Red Hat 4.8.5-39)]
1.2 Ansible 配置文件
1.2.1 配置文件
- /etc/ansible/ansible.cfg # 主配置文件, 用来配置Ansible功能
- /etc/ansible/hosts # 定义主机清单文件
- /etc/ansible/roles # 定义角色目录
1.2.2 Ansible 主配置文件
- /etc/ansible/ansible.cfg: 大部分内容都无需修改
[defaults]
# some basic default values...
#inventory = /etc/ansible/hosts # 定义主机清单
#library = /usr/share/my_modules/ # 库文件
#module_utils = /usr/share/my_module_utils/
#remote_tmp = ~/.ansible/tmp # 用来存放Python临时文件的目录, ansible推送时产生的临时文件会存放在被管理节点的这个目录里
#local_tmp = ~/.ansible/tmp # Python临时文件在ansible主机的存放目录
# 这两个临时目录会在ansible执行时, 自动创建
#plugin_filters_cfg = /etc/ansible/plugin_filters.yml
#forks = 5 # ansbile并发处理的数量, 默认为5, 也就是每次处理5个主机
#poll_interval = 15
#sudo_user = root # 默认的sudo用户, 该账户需要在被管理节点存在, 并且该账户要对目标主机有相应的管理权限. 如果不用root, 而是用其他账户, 比如admin, 那么admin需要在被管理节点存在, 并且在被管理节点具有sudo权限(admin all all all), 给予admin在被管理节点的全部sudo权限. 这样主控端利用admin连接到被管理节点后, 可以sudo切换成root用户执行命令
# 一般情况, 直接就用root管理即可, 提高管理效率. 本身ansible利用的就是ssh协议, 效率本身就有问题. 如果连接百台服务器, 再切换sudo, 那么效率就更低了, 而且还要提前创建账号, 配置sudo权限
#ask_sudo_pass = True # 当ansible使用上面的sudo账号连接到被管理节点时, 是否需要被管理节点的该sudo账户的密码
#ask_pass = True # ssh远程连接时是否需要远端用户的密码
#transport = smart
#remote_port = 22 # 被管理节点ssh端口号
#module_lang = C
#module_set_locale = False
#host_key_checking = False # 第一次连接某个主机时, 是否需要手动输入yes, 才下载对端主机公钥. False表示无需手动输入yes, 需要取消注释才会生效
# 还可以修改ansible管理端主机的/etc/ssh/ssh_config文件(#StrictHostKeyChecking ask --> StrictHostKeyChecking no )
# 还可以在执行时使用`ssh IP -o StrictHostKeyChecking=no`
#log_path=/var/log/ansible.log # ansible日志, 建议开启
#module_name= command # ansible执行命令时, 默认使用的模块, 改成shell比较好
1.2.3 主机清单
格式:
域名
IP
[名称]
IP
域名
...
[名称]
IP1
IP2
...
[websrvs]
www[1:100].example.com
[dbsrvs]
db-[a:f].example.com
[appsrvs]
10.0.0.[1:100] # 表示10.0.0.1, 10.0.0.2, 10.0.0.3 ... 10.0.0.100
本文案例:
# ansible主机: 10.0.0.237
# 被管理节点
CentOS-8
10.0.0.85
10.0.0.86
CentOS-7
10.0.0.187
[websrvs]
10.0.0.85
10.0.0.86
[appsrvs]
10.0.0.187
10.0.0.85
[dbsrvs]
10.0.0.187
10.0.0.86
1.3 Ansible 工具
[18:06:08 root@ansible ~]#ansible
ansible ansible-config ansible-console-2 ansible-doc-2 ansible-galaxy-2 ansible-playbook ansible-pull ansible-vault
ansible-2 ansible-connection ansible-console-2.7 ansible-doc-2.7 ansible-galaxy-2.7 ansible-playbook-2 ansible-pull-2 ansible-vault-2
ansible-2.7 ansible-console ansible-doc ansible-galaxy ansible-inventory ansible-playbook-2.7 ansible-pull-2.7 ansible-vault-2.7
- /usr/bin/ansible # 主程序, 临时命令执行工具
- /usr/bin/ansible-doc # 查看配置文档, 模块功能查看工具
- /usr/bin/ansible-playbook # 定制自动化任务, 编排剧本工具, 相当于脚本
- /usr/bin/ansible-pull # 远程执行命令的工具
- /usr/bin/ansible-vault # 文件加密工具
- /usr/bin/ansible-console # 基于Console界面与用于交互的执行工具
- /usr/bin/ansible-galaxy # 下载/上传优秀代码或Roles模块的官网平台
利用ansible实现管理的主要方式
- Ad-Hoc: 利用ansible命令, 临时执行任务
- Ansible-playbook: 主要用于长期规划好的, 大型项目的场景, 需要前期规划好
1.3.1 ansible-doc
- 查看可用模块
ansible-doc -l
- 查看某个模块的使用方法
ansible-doc -s MODULES_NAME, 简要描述
ansible-doc MODULES_NAME, 详细使用方法
ping模块只是检测某个主机是否可以被ansible管理, 并不是执行icmp协议的ping命令
[12:15:56 root@ansible ~]#ansible-doc -s ping
- name: Try to connect to host, verify a usable python and return `pong' on success
ping:
data: # Data to return for the `ping' return value. If this parameter is set to `crash', the module will cause an exception.
1.3.2 利用sshpass配置基于key的ssh认证
ansible通过ssh协议,实现对远程主机的配置管理、应用部署、任务执行等功能
建议:使用此工具前,先配置ansible主控端能基于密钥认证的方式连接各个被管理节点
如果是基于key验证, 那么第一次连接被管理节点时, 还需要手动输入是否将对端公钥下载到本地
如果是基于密码验证, 一旦被管理节点的密码不同, 那么就没法做到统一管理
范例: 修改ansible配置文件, 取消首次ssh下载公钥的确认
[21:21:47 root@ansible ~]#vim /etc/ansible/ansible.cfg
# uncomment this to disable SSH key host checking
host_key_checking = False
[21:40:13 root@ansible ~]#vim /etc/ansible/hosts
[websrvs]
10.0.0.85
10.0.0.86
[appsrvs]
10.0.0.187
10.0.0.85
[dbsrvs]
10.0.0.187
10.0.0.86
[21:38:39 root@ansible ~]#ansible all -m ping
10.0.0.85 | UNREACHABLE! => {
"changed": false,
"msg": "Failed to connect to the host via ssh: Warning: Permanently added '10.0.0.85' (ECDSA) to the list of known hosts.\r\nPermission denied (publickey,gssapi-keyex,gssapi-with-mic,password).",
"unreachable": true
}
10.0.0.187 | UNREACHABLE! => {
"changed": false,
"msg": "Failed to connect to the host via ssh: Warning: Permanently added '10.0.0.187' (ECDSA) to the list of known hosts.\r\nPermission denied (publickey,gssapi-keyex,gssapi-with-mic,password).",
"unreachable": true
}
10.0.0.86 | UNREACHABLE! => {
"changed": false,
"msg": "Failed to connect to the host via ssh: Warning: Permanently added '10.0.0.86' (ECDSA) to the list of known hosts.\r\nPermission denied (publickey,gssapi-keyex,gssapi-with-mic,password).",
"unreachable": true
}
[21:38:42 root@ansible ~]#cat .ssh/known_hosts
10.0.0.187 ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBHbDLtLE1uBBbjdaPQA0OB/doEFXlwmzi4eQ8YwKIfHKQm/XcLk3U8kEOW1A5eIWrMnhjmoD7JU/J/4eMkcr7cc=
10.0.0.85 ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBFF9+KPILpkSQfzg5/11sFpohPFd8XPretJMBzSCD/I/7rNs6Q85IJQzWquZy9KHyCBkyJJjz6lWHVJal5DCCjY=
10.0.0.86 ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBFF9+KPILpkSQfzg5/11sFpohPFd8XPretJMBzSCD/I/7rNs6Q85IJQzWquZy9KHyCBkyJJjz6lWHVJal5DCCjY=
范例: ansible默认是基于key验证, 如果想使用密码验证, 那么就在ansible命令里加上-k
选项
[21:42:42 root@ansible ~]#ansible all -m ping -k
SSH Password:
不过基于密码验证时, 每次执行ansible命令, 只能输一个密码. 如果多台主机的密码不同, 那么只有密码匹配的那些主机才会执行命令, 不匹配的主机还要重新再执行
一旦ansible和某台主机的密码校验成功, 那么ansible会对该主机和密码进行缓存(重启后丢失), 下次再使用ansible去连接该被管理节点时, 虽然仍然提示要输密码, 但是可以直接敲回车, 不用真的输入密码
所以, 实际工作中, 不会基于密码进行验证.
范例: 基于sshpass的key验证
- 方法1:
[root@centos8 ~]#cat hosts.list
10.0.0.18
10.0.0.28
[root@centos8 ~]#vim push_ssh_key.sh
#!/bin/bash
rpm -q sshpass &> /dev/null || yum -y install sshpass
[ -f /root/.ssh/id_rsa ] || ssh-keygen -f /root/.ssh/id_rsa -P ''
export SSHPASS=000000
while read IP;do
sshpass -e ssh-copy-id -o StrictHostKeyChecking=no $IP
done < hosts.list
- 方法2:
#!/bin/bash
IPLIST="
10.0.0.85
10.0.0.86
10.0.0.187
"
rpm -q sshpass &> /dev/null || yum -y install sshpass
[ -f /root/.ssh/id_rsa ] || ssh-keygen -f /root/.ssh/id_rsa -P ''
export SSHPASS=000000 # 被管理节点, root账户的登录密码要一致
for IP in $IPLIST;do
sshpass -e ssh-copy-id -o StrictHostkeyChecking=no $IP # -o StrictHostkeyChecking=no 不再提示是否下载公钥, 直接把对方主机的公钥下载到本地~/.ssh/known_hosts
done
[12:21:54 root@ansible ~]#ansible all -m ping
10.0.0.187 | SUCCESS => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python"
},
"changed": false,
"ping": "pong"
}
10.0.0.86 | SUCCESS => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/libexec/platform-python"
},
"changed": false,
"ping": "pong"
}
10.0.0.85 | SUCCESS => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/libexec/platform-python"
},
"changed": false,
"ping": "pong"
}
1.3.3 列出主机清单
[12:22:00 root@ansible ~]#ansible websrvs --list-hosts
hosts (2):
10.0.0.85
10.0.0.86
[12:24:09 root@ansible ~]#ansible all --list-hosts
hosts (3):
10.0.0.187
10.0.0.85
10.0.0.86
1.3.4 Ansible用于匹配被控制的主机的列表
- 格式要求
不支持的格式:
ansible 10.0.0.8[5:6] -m ping
ansible "10.0.0.8[5:6]" -m ping
[12:24:14 root@ansible ~]#ansible 10.0.0.8[5:6] -m ping
[WARNING]: Could not match supplied host pattern, ignoring: 10.0.0.8
[WARNING]: No hosts matched, nothing to do
- 一一列举
ansible "10.0.0.236 10.0.0.237" -m ping
- 通配符
ansible '*' -m ping
ansible 192.168.1.* -m ping
ansible "srvs" -m ping
- 或关系:
:
在websrvs组或在appsrvs组里的主机
[12:25:29 root@ansible ~]#ansible websrvs:appsrvs --list-hosts
hosts (3):
10.0.0.85
10.0.0.86
10.0.0.187
- 逻辑与:
:&
在websrvs组并且也在dbsrvs组里的主机
[12:29:30 root@ansible ~]#ansible 'websrvs:&appsrvs' --list-hosts
hosts (1):
10.0.0.85
- 逻辑非:
:!
在websrvs组, 但不在dbsrvs组中的主机
注意: 此处必须用单引号
[12:29:37 root@ansible ~]#ansible 'websrvs:!dbsrvs' --list-hosts
hosts (1):
10.0.0.85
- 综合逻辑
ansible 'websrvs:dbsegs:&appsrvs:!ftpsrvs' -m ping
- 正则表达式
ansible "~(web|db).*\.linux\.com" -m ping # ~符号是格式要求, 必须加
eg: 在k8s环境中, 如果ansible本机也被加入到了host清单里, 那么当k8s配置完主机, 需要批量重启远程主机时, 需要把本机先刨除在ansible命令执行清单外. 避免如果本机先重启了那么这条ansible命令也就不会执行后续重启其他主机的操作了
ansible 'kube*:etcd:!10.0.0.101' -a reboot; reboot
ansible命令执行过程
- 加载自己的配置文件, 默认/etc/ansible/ansible.cfg
- 加载自己对应的模块文件, 如: command
- 通过ansible, 为要在远程主机运行的模块或命令, 生成对应的临时py文件, 这些临时文件会先存储在ansible主控端的
~/.ansible/tmp
目录下, 然后会被传输至远程服务器的对应执行用户的家目录,$HOME/.ansible/tmp/ansible-tmp-数字/XXX.py
- 给远程主机的临时文件添加执行权限
- 执行并返回结果
- 删除本地和远程主机上的临时py文件, 退出
ansible执行状态
绿色: 执行成功并且不需要做改变的操作
黄色: 执行成功并且对目标主机做了变更, 或者仅表示信息查看成功, 没有做修改
红色: 执行失败
颜色可以在配置文件自定义
2 Ansible 常用模块
2.1 command 模块
- ansible的默认模块, 执行命令时不指定-m,那么就是使用command模块
- command模块只支持基本的Linux命令
- 不支持特殊符号,如(
$VARNAME < > | ; & ...
), 使用特殊符号需要用shell模块 - 命令需要写在单引号或者双引号里,
ansible all -m command -a 'cat /etc/issue'
- -a表示模块的参数, 适用于ansible所有模块
[13:55:48 root@ansible ~]#ansible-doc -s command
- name: Execute commands on targets
command:
argv: # Passes the command as a list rather than a string. Use `argv'
to avoid quoting values that
would otherwise be interpreted
incorrectly (for example "user
name"). Only the string or the
list form can be provided, not
both. One or the other must be
provided.
chdir: # Change into this directory before running the command.
cmd: # The command to run.
creates: # A filename or (since 2.0) glob pattern. If it already exists,
this step *won't* be run.
free_form: # The command module takes a free form command to run. There is
no actual parameter named 'free
form'.
removes: # A filename or (since 2.0) glob pattern. If it already exists,
this step *will* be run.
stdin: # Set the stdin of the command directly to the specified value.
stdin_add_newline: # If set to `yes', append a newline to stdin data.
strip_empty_ends: # Strip empty lines from the end of stdout/stderr in result.
warn: # Enable or disable task warnings.
eg: 执行简单shell命令
[12:37:10 root@ansible ~]#ansible all -m command -a 'cat /etc/centos-release '
10.0.0.187 | CHANGED | rc=0 >>
CentOS Linux release 7.8.2003 (Core)
10.0.0.86 | CHANGED | rc=0 >>
CentOS Linux release 8.2.2004 (Core)
10.0.0.85 | CHANGED | rc=0 >>
CentOS Linux release 8.2.2004 (Core)
eg: command 模块不支持特殊符号
[12:37:27 root@ansible ~]#ansible all -m command -a 'ls /data/*'
10.0.0.187 | FAILED | rc=2 >>
ls: cannot access /data/*: No such file or directorynon-zero return code
10.0.0.85 | FAILED | rc=2 >>
ls: cannot access '/data/*': No such file or directorynon-zero return code
10.0.0.86 | FAILED | rc=2 >>
ls: cannot access '/data/*': No such file or directorynon-zero return code
[12:39:01 root@ansible ~]#ansible all -m command -a 'echo $HOSTNAME' # ansible中查看对端主机上某个变量的值时, 需要使用单引号, 不过command模块不支持, 要用shell模块
10.0.0.187 | CHANGED | rc=0 >>
$HOSTNAME
10.0.0.86 | CHANGED | rc=0 >>
$HOSTNAME
10.0.0.85 | CHANGED | rc=0 >>
$HOSTNAME
[12:39:34 root@ansible ~]#ansible all -m command -a "echo $HOSTNAME" # ansible中查看对端主机上某个变量的值时, 需要使用单引号, 双引号会显示变量在本机的值
10.0.0.187 | CHANGED | rc=0 >>
ansible
10.0.0.85 | CHANGED | rc=0 >>
ansible
10.0.0.86 | CHANGED | rc=0 >>
ansible
[14:08:01 root@ansible ~]#ansible all -a "ls /boot > /data/ls.log"
#command模块不支持重定向, 因为>也属于特殊符号
2.2 shell 模块
- 类似command模块, 但是支持特殊符号, 通配符,
$VAR, <, >,...
- 对于变量, 需要写在单引号
- shell模块无法支持复杂的命令, 复杂命令需要使用script模块
范例:
[12:44:54 root@ansible ~]#ansible all -m shell -a "echo $HOSTNAME"
10.0.0.187 | CHANGED | rc=0 >>
ansible
10.0.0.85 | CHANGED | rc=0 >>
ansible
10.0.0.86 | CHANGED | rc=0 >>
ansible
[12:45:10 root@ansible ~]#ansible all -m shell -a 'echo $HOSTNAME'
10.0.0.187 | CHANGED | rc=0 >>
centos-7-6.prac
10.0.0.86 | CHANGED | rc=0 >>
CentOS-8-6
10.0.0.85 | CHANGED | rc=0 >>
CentOS-8-5
[12:45:27 root@ansible ~]#ansible all -m shell -a "ls /boot > /data/ls.log"
10.0.0.187 | CHANGED | rc=0 >>
10.0.0.85 | CHANGED | rc=0 >>
10.0.0.86 | CHANGED | rc=0 >>
[12:00:07 root@CentOS-8-5 ~]#ls /data
ls.log
ansible log: /var/log/ansible.log, 需要在配置文件中取消注释才能生效
chdir: 执行命令前, 先切换到某个目录, 相当于 cd /dir; CMD
格式: chdir=/PATH 命令
[12:47:43 root@ansible ~]#ansible all -m shell -a 'chdir=/data ls'
10.0.0.187 | CHANGED | rc=0 >>
ls.log
pkgs
prac
scripts
10.0.0.86 | CHANGED | rc=0 >>
ls.log
pkgs
prac
scripts
10.0.0.85 | CHANGED | rc=0 >>
ls.log
pkgs
prac
scripts
creates: 如果指定的文件或者目录存在, 则不会执行后续命令. 如果指定的文件或者目录不存在, 则会执行后续命令
[12:49:22 root@ansible ~]#ansible all -a 'creates=/etc ls -l /etc/issue'
10.0.0.187 | SUCCESS | rc=0 >> # /etc目录存在, 所以不会执行ls -l /etc/issue
skipped, since /etc exists
10.0.0.86 | SUCCESS | rc=0 >>
skipped, since /etc exists
10.0.0.85 | SUCCESS | rc=0 >>
skipped, since /etc exists
[12:50:09 root@ansible ~]#ansible all -a 'creates=/etc/issue ls -l /etc/issue'
10.0.0.187 | SUCCESS | rc=0 >>
skipped, since /etc/issue exists
10.0.0.85 | SUCCESS | rc=0 >>
skipped, since /etc/issue exists
10.0.0.86 | SUCCESS | rc=0 >>
skipped, since /etc/issue exists
[19:49:20 root@CentOS-8-5 ~]#touch creates.log
[22:40:54 root@ansible ~]#ansible all -a 'creates=creates.log ls /data'
10.0.0.187 | CHANGED | rc=0 >> # 10.0.0.187的root家目录, 没有creates.log文件, 所以会执行后续命令
pkgs
prac
scripts
test.log
10.0.0.85 | SUCCESS | rc=0 >> # 10.0.0.85的root家目录, 有creates.log文件, 所以不会执行后续命令
skipped, since creates.log exists
10.0.0.86 | CHANGED | rc=0 >>
pkgs
prac
scripts
test.log
removes: 如果指定的文件或目录存在, 则执行后续命令
[12:52:22 root@ansible ~]#ansible all -a 'removes=/etc/issue ls -l /etc/issue'
10.0.0.187 | CHANGED | rc=0 >>
-rw-r--r--. 1 root root 23 Apr 8 2020 /etc/issue
10.0.0.85 | CHANGED | rc=0 >>
-rw-r--r--. 1 root root 23 Jun 3 2020 /etc/issue
10.0.0.86 | CHANGED | rc=0 >>
-rw-r--r--. 1 root root 23 Jun 3 2020 /etc/issue
2.3 script 模块
- 在被管理节点执行脚本, 脚本只需要在管理节点存在即可
- 脚本在ansible本机或者远程主机都无需执行权限
- 脚本在管理节点的路径必须指定, 可以是绝对路径或者相对路径
eg: 执行test.sh脚本
test.sh
#!/bin/bash
echo $HOSTNAME
[12:56:15 root@ansible ~]#ansible all -m script -a '/root/test.sh' # 直接把脚本路径写到-a后的参数即可
10.0.0.187 | CHANGED => {
"changed": true,
"rc": 0,
"stderr": "Shared connection to 10.0.0.187 closed.\r\n",
"stderr_lines": [
"Shared connection to 10.0.0.187 closed."
],
"stdout": "centos-7-6.prac\r\n", # 返回结果
"stdout_lines": [
"centos-7-6.prac"
]
}
10.0.0.85 | CHANGED => {
"changed": true,
"rc": 0,
"stderr": "Shared connection to 10.0.0.85 closed.\r\n",
"stderr_lines": [
"Shared connection to 10.0.0.85 closed."
],
"stdout": "CentOS-8-5\r\n",
"stdout_lines": [
"CentOS-8-5"
]
}
10.0.0.86 | CHANGED => {
"changed": true,
"rc": 0,
"stderr": "Shared connection to 10.0.0.86 closed.\r\n",
"stderr_lines": [
"Shared connection to 10.0.0.86 closed."
],
"stdout": "CentOS-8-6\r\n",
"stdout_lines": [
"CentOS-8-6"
]
}
2.4 copy 模块
- 将本地文件拷贝到远程节点
- 如果没有指定
mode='权限'
, 那么拷贝过去的文件的权限是644
, 目录是755
权限 - 由于ansible的幂等性, 除非文件内容做了修改, 否则无论执行了几次命令, ansible都不会再拷贝文件
src: 如果复制的是一个目录, 那么目录如果没有加/补全, 那么复制的是整个目录, 如果加了/补全, 那么复制的是目录里的内容, 而不是目录本身
# Local path to a file to copy to the remote server. This can be
absolute or relative. If path is
a directory, it is copied
recursively. In this case, if
path ends with "/", only inside
contents of that directory are
copied to destination.
Otherwise, if it does not end
with "/", the directory itself
with all contents is copied.
This behavior is similar to the
`rsync' command line tool.
dest: 远程目标路径必须是绝对路径, 并且如果源是目录那么目标也必须是目录; 此外, 如果目标主机已经有了相同的文件, 默认会覆盖, 可以加 backup=yes 来先备份目标文件
# (required) Remote absolute path where the file should be copied
to. If `src' is a directory,
this must be a directory too. If
`dest' is a non-existent path
and if either `dest' ends with
"/" or `src' is a directory,
`dest' is created. If `dest' is
a relative path, the starting
directory is determined by the
remote host. If `src' and `dest'
are files, the parent directory
of `dest' is not created and the
task fails if it does not
already exist.
eg: copy /data/scripts/test.sh to remote hosts
[15:03:06 root@ansible ~]#ansible all -m copy -a 'src=/data/scripts/test.sh dest=/data/scripts/test1.sh'
[15:03:13 root@ansible ~]#ansible all -m shell -a 'ls /data/scripts'
10.0.0.187 | CHANGED | rc=0 >>
test1.sh
10.0.0.85 | CHANGED | rc=0 >>
test1.sh
10.0.0.86 | CHANGED | rc=0 >>
test1.sh
owner: 可以指定owner参数, 但是用户必须在被管理节点事先存在, 否则文件或目录拷贝过去后owner是不会变的.
backup: 如果远程节点有相同的文件, 那么copy时可以指定'backup=yes', 这会创建一个备份文件. 当某个文件需要被经常修改时, 要加backup=yes参数, 否则新的文件copy过去后会把旧的文件覆盖了
范例:
- 先在被管理节点/opt目录下创建test.sh脚本
[15:18:42 root@ansible ~]#ansible all -m shell -a 'touch /opt/test.sh'
- 将管理节点的test.sh文件copy到被管理节点/opt下, 指定ower,mode和backup=yes
[15:20:38 root@ansible ~]#ansible all -m copy -a 'src=/root/test.sh dest=/opt mode=600 owner=daemon backup=yes'
[12:46:24 root@CentOS-8-5 ~]#ls -l /opt
total 4
-rw------- 1 daemon root 19 Jan 31 15:21 test.sh # copy过来的文件, 权限, owner都修改了
-rw-r--r-- 1 root root 0 Jan 31 15:19 test.sh.28220.2021-01-31@15:21:45~ # 原先的文件会被打上timestamp
content: 将命令行的内容, 写入到被管理节点目标文件, 目标文件如果不存在, 会自动创建
范例:
[15:25:44 root@ansible ~]#ansible all -m copy -a 'content="line1\nline2\nline3" dest=/opt/test.txt'
[15:21:25 root@centos-7-6 ~]#ll /opt
total 8
drwxr-xr-x 5 root root 45 Jan 31 15:05 data
-rw------- 1 daemon root 19 Jan 31 15:21 test.sh
-rw-r--r-- 1 root root 0 Jan 31 15:19 test.sh.29262.2021-01-31@15:21:46~
-rw-r--r-- 1 root root 17 Jan 31 15:25 test.txt
[15:25:54 root@centos-7-6 ~]#cat /opt/test.txt
line1
line2
line3
copy 目录:
[15:28:36 root@ansible ~]#ansible all -m copy -a 'src=/tmp/ dest=/tmp'
[15:29:22 root@centos-7-6 ~]#ls /tmp
file.txt
[15:28:36 root@ansible ~]#ansible all -m copy -a 'src=/tmp dest=/tmp'
[15:29:22 root@centos-7-6 ~]#ls /tmp
file.txt tmp
2.5 fetch 模块
- 从被管理节点抓取文件到ansible主机的目录, 目前不支持抓取整个目录, 只能抓文件
- fetch的源只能是文件, 目前不支持抓目录, 也就是说fetch的源是管理节点的文件, 而目标是ansible主机的一个目录, 如果是从多台主机抓文件, 那么会在ansible目标目录按照抓取主机的ip地址生成各自的文件夹, 防止抓取同名文件覆盖
范例: 从被管理节点抓取/etc/issue文件到/opt目录下
[15:41:57 root@ansible ~]#ansible all -m fetch -a 'src=/etc/issue dest=/opt'
源文件存放的上级目录也会被抓取和创建, 相当于抓的整个路径
[15:42:22 root@ansible ~]#tree /opt
/opt
├── 10.0.0.187
│ └── etc
│ └── issue
├── 10.0.0.85
│ └── etc
│ └── issue
└── 10.0.0.86
└── etc
└── issue
6 directories, 3 files
2.6 file 模块
- 配置被管理节点上的文件属性和目录属性
- 也可以用来在被管理节点创建和删除文件
- 用
path=/PATH/文件名
来指定文件路径和名称 - state支持: absent, directory, file, hard, link, touch
touch: 创建文件, 要求上级目录已经存在, 否则会报错
absent: 如果文件存在则删除
directory:
file: 仅显示文件状态
返回state=absent: 说明文件在目标节点不存在
返回state=file: 说明文件在目标节点不存在
范例: state=file, 只会显示文件的状态
[15:45:57 root@ansible ~]#ansible all -m file -a 'path=/etc/issue state=file'
10.0.0.187 | SUCCESS => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python"
},
"changed": false,
"gid": 0,
"group": "root",
"mode": "0644",
"owner": "root",
"path": "/etc/issue",
"size": 23,
"state": "file",
"uid": 0
}
10.0.0.85 | SUCCESS => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/libexec/platform-python"
},
"changed": false,
"gid": 0,
"group": "root",
"mode": "0644",
"owner": "root",
"path": "/etc/issue",
"size": 23,
"state": "file",
"uid": 0
}
10.0.0.86 | SUCCESS => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/libexec/platform-python"
},
"changed": false,
"gid": 0,
"group": "root",
"mode": "0644",
"owner": "root",
"path": "/etc/issue",
"size": 23,
"state": "file",
"uid": 0
}
state=touch - 创建文件, 但是需要所在路径存在, 否则命令执行会失败
state=absent - 删除文件
范例: 修改被管理节点的/data/yum.conf文件属性
- 先将ansible节点的yum.conf文件copy到被管理节点
[15:52:15 root@ansible ~]#ansible all -m copy -a 'src=/etc/yum.conf dest=/data'
[15:52:38 root@ansible ~]#ansible all -m shell -a 'ls -l /data'
10.0.0.187 | CHANGED | rc=0 >>
total 4
-rw-r--r-- 1 root root 970 Jan 31 15:52 yum.conf
10.0.0.85 | CHANGED | rc=0 >>
total 4
-rw-r--r-- 1 root root 970 Jan 31 15:52 yum.conf
10.0.0.86 | CHANGED | rc=0 >>
total 4
-rw-r--r-- 1 root root 970 Jan 31 15:52 yum.conf
- 修改文件属性
[15:54:48 root@ansible ~]#ansible all -m file -a 'path=/data/yum.conf mode=600 owner=daemon'
[15:54:48 root@ansible ~]#ansible all -m shell -a 'ls -l /data'
10.0.0.187 | CHANGED | rc=0 >>
total 4
-rw------- 1 daemon root 970 Jan 31 15:52 yum.conf
10.0.0.86 | CHANGED | rc=0 >>
total 4
-rw------- 1 daemon root 970 Jan 31 15:52 yum.conf
10.0.0.85 | CHANGED | rc=0 >>
total 4
-rw------- 1 daemon root 970 Jan 31 15:52 yum.conf
如果不涉及从ansible复制文件到目标主机, 只是修改目标主机的文件或者目录, 那么只需要path即可, 后面加state=MODE
范例:
#创建空文件
ansible all -m file -a 'path=/data/test.txt state=touch' #state=touch不会递归创建目录, 因此要求路径必须已经存在
ansible all -m file -a 'path=/data/test.txt state=absent' #absent,如果文件存在则删除
ansible all -m file -a 'path=/root/test.sh owner=wang mode=755'
#创建目录
ansible all -m file -a 'path=/data/mysql state=directory owner=mysql' group=mysql' # directory会递归创建父目录
#创建软连接
ansible all -m file -a 'src=/data/testfile path|dest|name=/data/testfile-link state=link' # src是被管理节点本地的文件, 因为file就是管理远程节点的文件或目录
#创建目录
ansible all -m file -a 'path=/data/testdir state=directory'
2.7 unarchive 模块
- 解包解压文件
实现有两种用法:
- 1 将ansible主机上的压缩包传到远程主机后解压缩至指定目录, 设置copy=yes
- 2 将远程主机上的某个压缩包解压缩到指定的路径下, 设置copy=no
常见参数:
copy: 默认为yes, 当copy=yes, 拷贝的文件是从ansible主机复制到远程主机上, 如果设置为copy=no, 那么会在远程主机上寻找src源文件
remote_src: 和copy功能一样且互斥, yes表示在远程主机, 不在ansible主机, no表示文件在ansible主机上
src: 源文件, 可以是ansible主机上的路径, 也可以是远程主机上的路径, 如果是远程主机上的路径则需要设置copy=no
dest: 远程主机上的目标路径
mode: 设置解压缩后的文件权限
# 注意: 将ansible主机上的压缩包, 复制到目标主机的路径必须是存在的, 不存在会报错, 要手动创建
ansible all -m unarchive -a 'src=/data/foo.tgz dest=/var/lib owner=daemon group=bin'
ansible all -m unarchive -a 'src=/tmp/foo.zip dest=/data copy=no mode=0777'
# 也可以直接把互联网上的文件, 直接下载到被管理节点, 此时copy=no, src的值为互联网的文件的路径
ansible all -m unarchive -a 'src=https://www.baidu.com/image.jpg dest=/data copy=no'
2.8 archive 模块
将远程主机上的文件打包压缩到指定的路径
可以配合fetch模块, 先将远程主机上的文件打包, 然后抓取到ansible管理节点
范例:
#archive
[16:04:17 root@ansible ~]#ansible all -m archive -a 'path=/var/log dest=/tmp/log.tar.bz2 format=bz2 mode=600' # archive可以指定权限
#fetch
[16:04:51 root@ansible ~]#ansible all -m fetch -a 'src=/tmp/log.tar.bz2 dest=/data/prac'
[16:05:53 root@ansible ~]#ll /data/prac
total 0
drwxr-xr-x 3 root root 17 Jan 31 16:05 10.0.0.187
drwxr-xr-x 3 root root 17 Jan 31 16:05 10.0.0.85
drwxr-xr-x 3 root root 17 Jan 31 16:05 10.0.0.86
[16:05:55 root@ansible ~]#tree /data/prac
/data/prac
├── 10.0.0.187
│ └── tmp
│ └── log.tar.bz2
├── 10.0.0.85
│ └── tmp
│ └── log.tar.bz2
├── 10.0.0.86
│ └── tmp
│ └── log.tar.bz2
2.9 hostname 模块
- 无法批量修改主机名, 每次只能修改一个
- 支持多版本, 会自动识别不同版本系统存放主机名的文件, 并且修改该文件, 并且立即永久生效
CentOS6: /etc/sysconfig/network
CentOS7-8: /etc/network
范例:
[16:28:56 root@ansible ~]#ansible 10.0.0.100 -m hostname -a 'name=node1.com'
2.10 cron 模块
功能: 定义远程管理主机上执行计划任务
支持时间: minute, hour, day, month, weekday
如果计划任务是执行脚本, 那么要求远程主机上有该脚本, 或者先在ansible上编写脚本, 再通过copy拷贝到远程主机
范例:
#创建任务
ansible 10.0.0.238 -m cron -a 'hour=2 minute=30 weekday=1-5 name="backup mysql" job=/root/mysql_backup.sh'
#hour=2 表示2点
#minute=30 表示2点半
#weekday=1-5 表示周一到周五
#name 给计划任务起名
#job 实际执行的脚本
执行命令后, 会在管理节点的/etc/crontab里面创建计划任务
禁用计划任务
ansible websrvs -m cron -a 'hour=2 minute=30 weekday=1-5 name="backup mysql" job=/root/mysql_backup.sh disabled=yes'
启用计划任务
ansible websrvs -m cron -a 'hour=2 minute=30 weekday=1-5 name="backup mysql" job=/root/mysql_backup.sh disabled=no'
删除计划任务
ansible websrvs -m cron -a 'name="backup mysql" state=absent'
ansible websrvs -m crom -a 'name=backup mysql state=absent'
2.11 yum和apt 模块
红帽系统用yum模块
Ubuntu用apt
需要仓库在目标节点提前配好
范例: websrvs组安装httpd
[14:39:31 root@ansible ~]#ansible websrvs -m yum -a 'name=httpd state=present' # present是安装,默认就是present安装, absent是卸载
范例: websrvs组卸载httpd
[14:39:31 root@ansible ~]#ansible websrvs -m yum -a 'name=httpd state=absent'
范例: 同时安装多个软件
[00:30:59 root@ansible ~]#ansible all -m yum -a 'name=nginx,httpd state=present'
2.12 service 模块
- 管理远程主机上的service 服务
管理目标主机上的服务, 设置为开机启动, 停止, 重启等
ansible all -m service -a 'name=httpd state=started enabled=yes' # 启动httpd, 设置开启自启
ansible all -m service -a 'name=httpd state=stopped' # 关闭httpd
ansible all -m service -a 'name=httpd state=reloaded' # 重启httpd
ansible all -m shell -a "sed -i 's/^Listen 80/Listen 8080/ /etc/httpd/conf/httpd.conf'" # 批量修改配置文件
2.13 user 模块
管理用户: 批量创建, 删除同一个用户
#创建用户
ansible all -m user -a 'name=user1 comment="test user" uid=2048 home=/app/user1 group=root'
ansible all -m user -a 'name=nginx comment=nginx uid=88 group=nginx groups="root, daemon" shell=/sbin/nologin system=yes create_home=no home=/data/nginx non_unique=yes' # non_uniqu=yes: 忽略同名用户引起的冲突
#state=absent表示删除用户
#remove=yes表示删除用户家目录数据,默认remove=no
ansible all -m user -a 'name=nginx state=absent remove=yes'
2.14 group 模块
管理组: 批量创建, 删除同一个组
#创建组
ansible websrvs -m group -a 'name=nginx gid=88 system=yes'
#删除组
ansible websrvs -m group -a 'name=nginx state=absent'
2.15 lineinfile 模块
ansible在使用sed时经常会遇到需要转义的问题, 而且ansible在遇到特殊符号进行替换时存在问题, 无法正常进行替换, 其实在ansible自身还提供了两个模块, lineinfile模块和replace模块
一般在ansible中当去修改某个文件的单行进行替换的时候需要使用lineinfile
regexp参数: 使用正则表达式匹配对应的行, 进行替换文本时, 如果有多行文本都能被匹配, 则只有最后面被匹配到的那行才会被替换, 当删除文本时, 如果有多行文本都能被匹配, 这些行都会被删除, 如果想多行匹配替换, 建议用replace
ansible websrvs -m lineinfile -a "path=/etc/httpd/conf/httpd.conf regexp='^Listen' line='Listen 80'"
2.16 replace 模块
类似于sed命令, 基于正则匹配和替换, 会对匹配到的所有行进行操作
ansible all -m replace -a "path=/etc/fstab regexp='^(UUID.*)' replace='#\1'"
2.17 setup 模块
该模块用来收集主机的系统信息, 这些facts信息可以直接以变量的形式使用, 但是如果主机较多, 会影响执行速度, 可以使用gather_facts:no
来禁止ansible收集facts信息
通过这些信息, 在进行批量管理时, 可以根据每台服务器上的不同参数进行操作, 实现条件判断
范例:
ansible all -m setup #获取所有setup信息, 可以当做变量使用
ansible all -m setup -a 'filter=ansible_hostname' #获取指定的setup信息