Ansible命令执行过程
1.加载自己的配置文件,默认/etc/ansible/ansible.cfg
2.加载自己对应的模板文件,如command
3.通过ansible将模块或命令生成对应的临时py文件,并将该文件传输至远程服务器的对应执行用户 $HOME/.ansible/tmp/ansible-tmp-数字/xxx.py文件
4.给文件执行权限
5.执行并返回结果
6.删除临时py文件,退出
执行过程参数v可验证
[root@myserver_1 tmp]# grep chmod ansible.log
<192.168.1.38> SSH: EXEC ssh -C -o ControlMaster=auto -o ControlPersist=60s -o StrictHostKeyChecking=no -o KbdInteractiveAuthentication=no -o PreferredAuthentications=gssapi-with-mic,gssapi-keyex,hostbased,publickey -o PasswordAuthentication=no -o ConnectTimeout=10 -o ControlPath=/root/.ansible/cp/237d81d540 192.168.1.38 '/bin/sh -c '"'"'chmod u+x /root/.ansible/tmp/ansible-tmp-1609605284.73-63414-75711191811786/ /root/.ansible/tmp/ansible-tmp-1609605284.73-63414-75711191811786/AnsiballZ_ping.py && sleep 0'"'"''
<192.168.1.160> SSH: EXEC ssh -C -o ControlMaster=auto -o ControlPersist=60s -o StrictHostKeyChecking=no -o KbdInteractiveAuthentication=no -o PreferredAuthentications=gssapi-with-mic,gssapi-keyex,hostbased,publickey -o PasswordAuthentication=no -o ConnectTimeout=10 -o ControlPath=/root/.ansible/cp/649058737a 192.168.1.160 '/bin/sh -c '"'"'chmod u+x /root/.ansible/tmp/ansible-tmp-1609605284.76-63410-40006442659494/ /root/.ansible/tmp/ansible-tmp-1609605284.76-63410-40006442659494/AnsiballZ_ping.py && sleep 0'"'"''
<192.168.1.161> SSH: EXEC ssh -C -o ControlMaster=auto -o ControlPersist=60s -o StrictHostKeyChecking=no -o KbdInteractiveAuthentication=no -o PreferredAuthentications=gssapi-with-mic,gssapi-keyex,hostbased,publickey -o PasswordAuthentication=no -o ConnectTimeout=10 -o ControlPath=/root/.ansible/cp/1aa5cf8898 192.168.1.161 '/bin/sh -c '"'"'chmod u+x /root/.ansible/tmp/ansible-tmp-1609605284.89-63412-38250587576184/ /root/.ansible/tmp/ansible-tmp-1609605284.89-63412-38250587576184/AnsiballZ_ping.py && sleep 0'"'"''
<192.168.1.162> SSH: EXEC ssh -C -o ControlMaster=auto -o ControlPersist=60s -o StrictHostKeyChecking=no -o KbdInteractiveAuthentication=no -o PreferredAuthentications=gssapi-with-mic,gssapi-keyex,hostbased,publickey -o PasswordAuthentication=no -o ConnectTimeout=10 -o ControlPath=/root/.ansible/cp/f35ebaf285 192.168.1.162 '/bin/sh -c '"'"'chmod u+x /root/.ansible/tmp/ansible-tmp-1609605284.88-63413-236878805733374/ /root/.ansible/tmp/ansible-tmp-1609605284.88-63413-236878805733374/AnsiballZ_ping.py && sleep 0'"'"''
[root@myserver_1 tmp]# ll
total 28
-rw-r--r-- 1 root root 25889 Jan 3 00:34 ansible.log
查看主机清单
查看主机清单
[root@myserver_1 ~]# ansible all --list-hosts
hosts (4):
192.168.1.160
192.168.1.161
192.168.1.162
192.168.1.38
[root@myserver_1 ~]# ansible webservers --list-hosts
hosts (3):
192.168.1.160
192.168.1.161
192.168.1.162
[root@myserver_1 ~]# ansible dbservers --list-hosts
hosts (1):
192.168.1.38
[root@myserver_1 ~]#
ansible的逻辑非关系用单引号,其他逻辑关系可以用双引号
[root@myserver_1 ~]# ansible "webservers:!dbservers" -m ping
-bash: !dbservers": event not found
[root@myserver_1 ~]# ansible 'webservers:!dbservers' -m ping
192.168.1.161 | SUCCESS => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/libexec/platform-python"
},
"changed": false,
"ping": "pong"
}
192.168.1.162 | SUCCESS => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/libexec/platform-python"
},
"changed": false,
"ping": "pong"
}
192.168.1.160 | SUCCESS => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/libexec/platform-python"
},
"changed": false,
"ping": "pong"
}
[root@myserver_1 ~]#
支持正则表达式
[root@myserver_1 ~]# ansible "~(web|db)servers" -m ping
192.168.1.160 | SUCCESS => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/libexec/platform-python"
},
"changed": false,
"ping": "pong"
}
192.168.1.38 | SUCCESS => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python"
},
"changed": false,
"ping": "pong"
}
192.168.1.161 | SUCCESS => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/libexec/platform-python"
},
"changed": false,
"ping": "pong"
}
192.168.1.162 | SUCCESS => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/libexec/platform-python"
},
"changed": false,
"ping": "pong"
}
ansible反馈结果显示不同的颜色可以自定义 vim /etc/ansible/ansible.cfg
[colors]
#highlight = white
#verbose = blue
#warn = bright purple
#error = red
#debug = dark gray
#deprecate = purple
#skip = cyan
#unreachable = red
#ok = green
#changed = yellow
#diff_add = green
#diff_remove = red
#diff_lines = cyan
ansible-galaxy : 下载人家写好的playbook文本,根据需求改
https://galaxy.ansible.com/
下载自动解压缩
[root@myserver_1 mysql]# ansible-galaxy collection install community.mysql
解压缩的路径
[root@myserver_1 mysql]# pwd
/root/.ansible/collections/ansible_collections/community/mysql
[root@myserver_1 mysql]# ls
changelogs codecov.yml COPYING FILES.json MANIFEST.json meta plugins README.md tests
[root@myserver_1 mysql]#
列出已安装的galaxy
ansible-galaxy list
安装galaxy
ansible-galaxy install geerlingguy.redis
删除galaxy
ansible-galaxy remove geerlingguy.redis
ansible-playbook简单测试 广播发送
[root@myserver_1 ansible]# ansible-playbook hello.yml
PLAY [webservers] ****************************************************************************
TASK [Gathering Facts] ***********************************************************************
ok: [192.168.1.160]
ok: [192.168.1.161]
ok: [192.168.1.162]
TASK [hello world] ***************************************************************************
changed: [192.168.1.162]
changed: [192.168.1.161]
changed: [192.168.1.160]
PLAY RECAP ***********************************************************************************
192.168.1.160 : ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
192.168.1.161 : ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
192.168.1.162 : ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
[root@myserver_1 ansible]#
[root@myserver_1 ansible]# cat hello.yml
---
#hello world yml file
- hosts: webservers
remote_user: root
tasks:
- name: hello world
command: /usr/bin/wall hello world
[root@myserver_1 ansible]#
ansible-vault encrypt hello.yml #加密
ansible-vault decrypt hello.yml #解密
ansible-vault view hello.yml #查看
ansible-vault edit hello.yml #编辑
ansible-vault rekey hello.yml #修改口令
ansible-vault create new.yml #创建新文件
Ansible交互命令
[root@myserver_1 ansible]# ansible-console
Welcome to the ansible console.
Type help or ? to list commands.
root@all (4)[f:5]$ list
192.168.1.160
192.168.1.161
192.168.1.162
192.168.1.38
root@all (4)[f:5]$ cd webservers
root@webservers (3)[f:5]$ list
192.168.1.160
192.168.1.161
192.168.1.162
root@webservers (3)[f:5]$ yum name=httpd state=present
192.168.1.160 | CHANGED => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/libexec/platform-python"
},
"changed": true,
"msg": "",
"rc": 0,
"results": [
"Installed: apr-util-openssl-1.6.1-6.el8.x86_64",
"Installed: httpd-2.4.37-30.module_el8.3.0+561+97fdbbcc.x86_64",
"Installed: httpd-filesystem-2.4.37-30.module_el8.3.0+561+97fdbbcc.noarch",
"Installed: httpd-tools-2.4.37-30.module_el8.3.0+561+97fdbbcc.x86_64",
"Installed: centos-logos-httpd-80.5-2.el8.noarch",
"Installed: apr-1.6.3-11.el8.x86_64",
"Installed: apr-util-1.6.1-6.el8.x86_64",
"Installed: mod_http2-1.15.7-2.module_el8.3.0+477+498bb568.x86_64",
"Installed: apr-util-bdb-1.6.1-6.el8.x86_64"
]
}
192.168.1.161 | CHANGED => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/libexec/platform-python"
},
"changed": true,
"msg": "",
"rc": 0,
"results": [
"Installed: mod_http2-1.15.7-2.module_el8.3.0+477+498bb568.x86_64",
"Installed: httpd-2.4.37-30.module_el8.3.0+561+97fdbbcc.x86_64"
]
}
192.168.1.162 | CHANGED => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/libexec/platform-python"
},
"changed": true,
"msg": "",
"rc": 0,
"results": [
"Installed: mod_http2-1.15.7-2.module_el8.3.0+477+498bb568.x86_64",
"Installed: httpd-2.4.37-30.module_el8.3.0+561+97fdbbcc.x86_64"
]
}
ansible的常用模块 command 模块
command模块:在远程主机上执行,为默认模块,可忽略-m
ansible [-m module_name] [-a args]
查看系统版本号
[root@myserver_1 ansible]# ansible webservers -m command -a 'cat /etc/centos-release'
192.168.1.160 | CHANGED | rc=0 >>
CentOS Linux release 8.3.2011
192.168.1.162 | CHANGED | rc=0 >>
CentOS Linux release 8.3.2011
192.168.1.161 | CHANGED | rc=0 >>
CentOS Linux release 8.3.2011
[root@myserver_1 ansible]# ansible webservers -m command -a 'chdir=/etc cat centos-release'
192.168.1.161 | CHANGED | rc=0 >>
CentOS Linux release 8.3.2011
192.168.1.162 | CHANGED | rc=0 >>
CentOS Linux release 8.3.2011
192.168.1.160 | CHANGED | rc=0 >>
CentOS Linux release 8.3.2011
# -a后的命令chdir的命令其实就是下面的操作逻辑
[root@myserver_1 ansible]# cd /etc/;cat centos-release
CentOS Linux release 7.7.1908 (Core)
[root@myserver_1 etc]# ansible webservers -m command -a 'creates=/tmp/test.txt chdir=/etc cat centos-release'
192.168.1.162 | CHANGED | rc=0 >>
CentOS Linux release 8.3.2011
192.168.1.161 | CHANGED | rc=0 >>
CentOS Linux release 8.3.2011
192.168.1.160 | SUCCESS | rc=0 >>
skipped, since /tmp/test.txt exists
#creates文件存在执行,否则不执行,removes文件不存在执行,否则执行
#/tmp/test.txt文件在160上有,在另外两台没有
[root@myserver_1 etc]# ansible webservers -m command -a 'removes=/tmp/test.txt chdir=/etc cat centos-release'
192.168.1.161 | SUCCESS | rc=0 >>
skipped, since /tmp/test.txt does not exist
192.168.1.162 | SUCCESS | rc=0 >>
skipped, since /tmp/test.txt does not exist
192.168.1.160 | CHANGED | rc=0 >>
CentOS Linux release 8.3.2011
[root@myserver_1 etc]#
由于在command模块中不支持通配符,重定项,管道符等
[root@myserver_1 ansible]# ansible webservers -m command -a ' rm -rf /root/test/*'
[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.
192.168.1.161 | CHANGED | rc=0 >>
192.168.1.160 | CHANGED | rc=0 >>
192.168.1.162 | CHANGED | rc=0 >>
[root@myserver_1 ansible]# ansible webservers -m command -a ' ls /root/test/'
192.168.1.161 | CHANGED | rc=0 >>
123.txt
192.168.1.160 | CHANGED | rc=0 >>
123.txt
192.168.1.162 | CHANGED | rc=0 >>
123.txt
#只显示本机主机名不是远程主机的主机名
[root@myserver_1 ansible]# ansible webservers -m command -a "echo $HOSTNAME"
192.168.1.161 | CHANGED | rc=0 >>
myserver_1
192.168.1.162 | CHANGED | rc=0 >>
myserver_1
192.168.1.160 | CHANGED | rc=0 >>
myserver_1
[root@myserver_1 ansible]#
shell模块
Shell模块
命令和command类似,功能比command强大一点
[root@myserver_1 ansible]# ansible webservers -m shell -a 'echo $HOSTNAME'
192.168.1.160 | CHANGED | rc=0 >>
myserver
192.168.1.161 | CHANGED | rc=0 >>
myserver_1
192.168.1.162 | CHANGED | rc=0 >>
myserver_2
shell模块删除文件
[root@myserver_1 ansible]# ansible webservers -m shell -a 'rm -rf /root/test/*'
[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.
192.168.1.162 | CHANGED | rc=0 >>
192.168.1.160 | CHANGED | rc=0 >>
192.168.1.161 | CHANGED | rc=0 >>
[root@myserver_1 ansible]# ansible webservers -m command -a 'ls /root/test/'
192.168.1.161 | CHANGED | rc=0 >>
192.168.1.160 | CHANGED | rc=0 >>
192.168.1.162 | CHANGED | rc=0 >>
[root@myserver_1 ansible]#
Script模块
功能:在远程主机上运行ansible服务器上的脚本
ansible将脚本传到远程服务器执行,执行完后删掉
Copy模块
功能:从ansible服务器主控端复制文件到远程主机
即src是ansible服务器的路径,dest是远程主机的路径
[root@myserver_1 ~]# ansible webservers -m copy -a "content='test line1\ntest line2' dest=/tmp/test.txt"
192.168.1.162 | CHANGED => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/libexec/platform-python"
},
"changed": true,
"checksum": "43791ccbbcf72774b2bbbe6fe8d7ab488359b922",
"dest": "/tmp/test.txt",
"gid": 0,
"group": "root",
"md5sum": "f0e596e1a1a3ef7d278f2dda4d4e6ec8",
"mode": "0644",
"owner": "root",
"size": 21,
"src": "/root/.ansible/tmp/ansible-tmp-1609657262.1-68634-140320123278887/source",
"state": "file",
"uid": 0
}
192.168.1.160 | CHANGED => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/libexec/platform-python"
},
"changed": true,
"checksum": "43791ccbbcf72774b2bbbe6fe8d7ab488359b922",
"dest": "/tmp/test.txt",
"gid": 0,
"group": "root",
"md5sum": "f0e596e1a1a3ef7d278f2dda4d4e6ec8",
"mode": "0644",
"owner": "root",
"size": 21,
"src": "/root/.ansible/tmp/ansible-tmp-1609657261.97-68625-103834993013075/source",
"state": "file",
"uid": 0
}
192.168.1.161 | CHANGED => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/libexec/platform-python"
},
"changed": true,
"checksum": "43791ccbbcf72774b2bbbe6fe8d7ab488359b922",
"dest": "/tmp/test.txt",
"gid": 0,
"group": "root",
"md5sum": "f0e596e1a1a3ef7d278f2dda4d4e6ec8",
"mode": "0644",
"owner": "root",
"size": 21,
"src": "/root/.ansible/tmp/ansible-tmp-1609657262.12-68631-219826191059601/source",
"state": "file",
"uid": 0
}
[root@myserver_1 ~]# ansible webservers -m shell -a "cat /tmp/test.txt"
192.168.1.160 | CHANGED | rc=0 >>
test line1
test line2
192.168.1.161 | CHANGED | rc=0 >>
test line1
test line2
192.168.1.162 | CHANGED | rc=0 >>
test line1
test line2
[root@myserver_1 ~]#
拷贝到远程主机的文件
[root@myserver_1 ~]# ansible webservers -m copy -a "src=/etc/centos-release dest=/tmp/os.txt owner=docker mode=600"
Fetch模块
和copy模块功能相反,目前不支持拷贝目录
[root@myserver_1 ~]# ansible webservers -m copy -a "src=/etc/centos-release dest=/tmp/os.txt owner=docker mode=600"
到拷贝的目录下查看
[root@myserver_1 etc]# pwd
/tmp/os/192.168.1.160/etc
[root@myserver_1 etc]# ls
centos-release
[root@myserver_1 etc]#
File模块:设置文件的属性
[root@myserver_1 ~]# ansible webservers -m file -a 'path=/tmp/test.txt state=touch owner=docker group=docker mode=600'
unarchive: 解包解压缩
实现两种方式
1.将ansible主机上的压缩包传到远程主机后解压缩到特定目录,设置copy=yes
2.将远程主机上的某个压缩包压缩到指定路径下,设置copy=no
[root@myserver_1 tmp]# ansible webservers -m unarchive -a 'src=/tmp/test.tar.gz dest=/root/test/ owner=docker copy=yes'
[root@myserver_1 tmp]# ansible webservers -m unarchive -a 'src=/root/test.tar.gz
crontab模块的添加,注释与删除
#1.添加定时任务
[root@myserver_1 ~]# ansible dbservers -m cron -a 'hour=2 minute=30 weekday=5 name="backup.mysql" job=/root/mysql_backup.sh'
192.168.1.38 | CHANGED => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python"
},
"changed": true,
"envs": [],
"jobs": [
"backup.mysql"
]
}
[root@myserver_1 ~]# crontab -l
#Ansible: backup.mysql
30 2 * * 5 /root/mysql_backup.sh
#取消定时任务
[root@myserver_1 ~]# ansible dbservers -m cron -a 'hour=2 minute=30 weekday=5 name="backup.mysql" job=/root/mysql_backup.sh disabled=yes'
192.168.1.38 | CHANGED => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python"
},
"changed": true,
"envs": [],
"jobs": [
"backup.mysql"
]
}
[root@myserver_1 ~]# crontab -l
#Ansible: backup.mysql
#30 2 * * 5 /root/mysql_backup.sh
#开启定时任务
[root@myserver_1 ~]# ansible dbservers -m cron -a 'hour=2 minute=30 weekday=5 name="backup.mysql" job=/root/mysql_backup.sh disabled=no'
192.168.1.38 | CHANGED => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python"
},
"changed": true,
"envs": [],
"jobs": [
"backup.mysql"
]
}
[root@myserver_1 ~]# crontab -l
#Ansible: backup.mysql
30 2 * * 5 /root/mysql_backup.sh
#删除定时任务
[root@myserver_1 ~]# ansible dbservers -m cron -a 'hour=2 minute=30 weekday=5 name="backup.mysql" state=absent'
192.168.1.38 | CHANGED => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python"
},
"changed": true,
"envs": [],
"jobs": []
}
[root@myserver_1 ~]# crontab -l
[root@myserver_1 ~]#
YUM模块
Service模块
User模块
YUM模块的使用
[root@myserver_1 ~]# ansible all -m yum -a 'name=httpd state=present'
[root@myserver_1 ~]# ansible all -m yum -a 'name=httpd state=absent'
Service模块
#安装http服务
[root@myserver_1 ~]# ansible all -m yum -a 'name=httpd state=present'
#启动http服务
[root@myserver_1 ~]# ansible all -m service -a 'name=httpd state=started'
#查看运行状态
[root@myserver_1 ~]# ansible all -m shell -a 'systemctl status httpd'
#关闭http服务
[root@myserver_1 ~]# ansible all -m service -a 'name=httpd state=stopped'
User模块:管理用户
[root@myserver_1 ~]# ansible dbservers -m user -a 'name=user1 comment="test user" uid=2048 home=/app/user1 group=root'
192.168.1.38 | CHANGED => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python"
},
"changed": true,
"comment": "test user",
"create_home": true,
"group": 0,
"home": "/app/user1",
"name": "user1",
"shell": "/bin/bash",
"state": "present",
"stderr": "useradd: warning: the home directory already exists.\nNot copying any file from skel directory into it.\n",
"stderr_lines": [
"useradd: warning: the home directory already exists.",
"Not copying any file from skel directory into it."
],
"system": false,
"uid": 2048
}
[root@myserver_1 ~]# cd /app/user1/
[root@myserver_1 user1]# ls
删除用户
[root@myserver_1 app]# ansible dbservers -m user -a 'name=user1 state=absent remove=yes'
192.168.1.38 | SUCCESS => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python"
},
"changed": false,
"name": "user1",
"state": "absent"
}
[root@myserver_1 app]# ls
Linefile模块和replace模块功能类似sed
[root@myserver_1 selinux]# ansible all -m lineinfile -a "path=/etc/selinux/config regexp='^SELINUX=' line='SELINUX=enforcing'"
[root@myserver_1 selinux]# ansible all -m lineinfile -a "path=/etc/selinux/config regexp='^SELINUX=' line='SELINUX=disabled'"
Setup模块:收集远程主机的配置信息
[root@myserver_1 selinux]# ansible dbservers -m setup
查看系统版本信息
[root@myserver_1 selinux]# ansible all -m setup -a 'filter=ansible_distribution_major_version'
192.168.1.38 | SUCCESS => {
"ansible_facts": {
"ansible_distribution_major_version": "7",
"discovered_interpreter_python": "/usr/bin/python"
},
"changed": false
}
192.168.1.160 | SUCCESS => {
"ansible_facts": {
"ansible_distribution_major_version": "8",
"discovered_interpreter_python": "/usr/libexec/platform-python"
},
"changed": false
}
192.168.1.162 | SUCCESS => {
"ansible_facts": {
"ansible_distribution_major_version": "8",
"discovered_interpreter_python": "/usr/libexec/platform-python"
},
"changed": false
}
192.168.1.161 | SUCCESS => {
"ansible_facts": {
"ansible_distribution_major_version": "8",
"discovered_interpreter_python": "/usr/libexec/platform-python"
},
"changed": false
}
ansible-playbook
mysql_user.yml文件,用来创建mysql的用户和组
[root@myserver_1 ansible]# vim mysql_user.yml
---
- hosts: dbservers
remote_user: root
gather_facts: no
tasks:
- name: create group
group: name=mysql system=yes gid=306
- name: create user
user: name=mysql shell=/sbin/nologin system=yes group=mysql uid=306 home=/data/mysql create_home=no
检查yml语法正确与否
[root@myserver_1 ansible]# ansible-playbook -C mysql_user.yml
PLAY [dbservers] ********************************************************************************
TASK [create group] *****************************************************************************
changed: [192.168.1.38]
changed: [192.168.1.161]
TASK [create user] ******************************************************************************
changed: [192.168.1.38]
changed: [192.168.1.161]
PLAY RECAP **************************************************************************************
192.168.1.161 : ok=2 changed=2 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
192.168.1.38 : ok=2 changed=2 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
[root@myserver_1 ansible]#
执行查看结果
[root@myserver_1 ansible]# ansible-playbook mysql_user.yml
PLAY [dbservers] ********************************************************************************
TASK [create group] *****************************************************************************
changed: [192.168.1.38]
changed: [192.168.1.161]
TASK [create user] ******************************************************************************
fatal: [192.168.1.38]: FAILED! => {"changed": false, "msg": "usermod: user mysql is currently used by process 1238\n", "name": "mysql", "rc": 8}
changed: [192.168.1.161]
PLAY RECAP **************************************************************************************
192.168.1.161 : ok=2 changed=2 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
192.168.1.38 : ok=1 changed=1 unreachable=0 failed=1 skipped=0 rescued=0 ignored=0
[root@myserver_1 ansible]# ansible dbservers -m shell -a 'getent passwd mysql'
192.168.1.38 | CHANGED | rc=0 >>
mysql:x:27:306:MariaDB Server:/var/lib/mysql:/sbin/nologin
192.168.1.161 | CHANGED | rc=0 >>
mysql:x:306:306::/data/mysql:/sbin/nologin
[root@myserver_1 ansible]# ansible dbservers -m shell -a 'id mysql'
192.168.1.161 | CHANGED | rc=0 >>
uid=306(mysql) gid=306(mysql) groups=306(mysql)
192.168.1.38 | CHANGED | rc=0 >>
uid=27(mysql) gid=306(mysql) groups=306(mysql)