Ansible-基础及常用模块
安装
1. 准备yum源:
https://developer.aliyun.com/mirror/epel?spm=a2c6h.13651102.0.0.3fde1b11FsojqW
https://developer.aliyun.com/mirror/centos?spm=a2c6h.13651102.0.0.3e221b11CbELri
yum -y install ansible
2. 重要的配置文件
/etc/ansible/ansible.cfg # 配置文件 /etc/ansible/hosts # 主机清单
/etc/ansible/roles # 角色
修改ansible.cfg
[root@master-1 clash]# egrep -v "^#|^$" /etc/ansible/ansible.cfg [defaults] inventory = /etc/ansible/hosts host_key_checking = False log_path = /var/log/ansible.log module_name = shell # 修改默认模块为shell [inventory] [privilege_escalation] [paramiko_connection] [ssh_connection] [persistent_connection] [accelerate] [selinux] [colors] [diff]
3. 配置ssh免密登录
4. 命令格式
host-pattern:主机分组
-m:模块名
-a:模块参数
-f:任务分批处理,进程数量,默认是5个
--limit l 限制条件
限制只在某一台主机
ansible-playbook playbook.yml --limit webserver1.example.com
限制两主机组的并集(主机同时存在于这两主机组)
ansible-playbook playbook.yml --limit webserver1.example.com
限制在某个主机组执行,但不包含某台主机
ansible-playbook playbook.yml --limit webservers:!webserver1.example.com
ansible 常用命令
5. 寻求模块方法帮助
ansible-doc -l # 查看所有
ansible-doc -s ping # 查看ping模块如何使用
6. 分组主机
vim /etc/ansible/hosts # 单台主机 # node1.m8s.com node[1:2]
[nginx]
192.168.43.129
192.168.43.130
7. 查看所有分组及主机
返回为图表
ansible-inventory --graph @all: |--@haproxy-keepalived: | |--192.168.43.129 | |--192.168.43.132 | |--192.168.43.133 |--@k8s-master: | |--192.168.43.129 | |--192.168.43.132 | |--192.168.43.133 |--@k8s-node: | |--192.168.43.129 | |--192.168.43.130 | |--192.168.43.131 | |--192.168.43.132 | |--192.168.43.133 |--@mysql: | |--192.168.43.129 |--@nginx: | |--192.168.43.130 | |--192.168.43.131 |--@redis: | |--192.168.43.129 | |--192.168.43.130 | |--192.168.43.131 |--@ungrouped:
查看分组及服务,返回为json格式
[root@kafka-1 rc.d]# ansible-inventory --list { "_meta": { "hostvars": {} }, "all": { "children": [ "es", "mysql", "redis", "ungrouped", "web" ] }, "es": { "hosts": [ "192.168.64.16", "192.168.64.29" ] }, "mysql": { "hosts": [ "192.168.64.30" ] }, "redis": { "hosts": [ "192.168.64.32" ] }, "web": { "hosts": [ "192.168.64.12", "192.168.64.14" ] } }
jq 用法,取值
[root@kafka-1 opt]# ansible-inventory --list | jq .es { "hosts": [ "192.168.64.16", "192.168.64.29" ] }
[root@kafka-1 opt]# ansible-inventory --list | jq .es[] [ "192.168.64.16", "192.168.64.29" ] 查看主机列表 [root@kafka-1 rc.d]# ansible all --list-hosts hosts (6): 192.168.64.12 192.168.64.14 192.168.64.32 192.168.64.30 192.168.64.16 192.168.64.29 [root@kafka-1 rc.d]# ansible web --list-hosts hosts (2): 192.168.64.12 192.168.64.14 ansible-inventory --list |jq .all
{ "children": [ "haproxy-keepalived", "k8s-master", "k8s-node", "mysql", "nginx", "redis", "ungrouped" ] }
8. 多进程运行
默认情况下,Ansible 仅使用5个同时运行的进程。如果您拥有的主机数量多于为 fork 计数设置的值,则可能会增加 Ansible 与主机通信所需的时间。
要重新启动具有 10 个并行 fork 的 [atlanta] 服务器:
ansible atlanta -a "/sbin/reboot" -f 10
9. 自定义错误返回
# 部分主机返回 non-zero return code 错误,这表示在这些主机上执行的命令没有成功
# grep 80 不会找到任何匹配的行,因此 grep 返回的退出状态码会是 1
[root@kafka-1 sbin]# ansible all -a 'netstat -ntpl |grep 80 ||The nginx 80 port is not listening' 192.168.64.12 | FAILED | rc=127 >> /bin/sh: The: command not foundnon-zero return code 192.168.64.14 | CHANGED | rc=0 >> tcp 0 0 0.0.0.0:80 0.0.0.0:* LISTEN 17073/nginx: master tcp6 0 0 :::9090 :::* LISTEN 803/prometheus 192.168.64.16 | FAILED | rc=127 >> /bin/sh: The: command not foundnon-zero return code 192.168.64.32 | FAILED | rc=127 >> /bin/sh: The: command not foundnon-zero return code 192.168.64.30 | FAILED | rc=127 >> /bin/sh: The: command not foundnon-zero return code 192.168.64.29 | FAILED | rc=127 >> /bin/sh: The: command not foundnon-zero return code [root@kafka-1 sbin]# ansible all -a 'netstat -ntpl |grep 80 ||echo "The nginx 80 port is not listening"' 192.168.64.12 | CHANGED | rc=0 >> The nginx 80 port is not listening 192.168.64.14 | CHANGED | rc=0 >> tcp 0 0 0.0.0.0:80 0.0.0.0:* LISTEN 17073/nginx: master tcp6 0 0 :::9090 :::* LISTEN 803/prometheus 192.168.64.32 | CHANGED | rc=0 >> The nginx 80 port is not listening 192.168.64.16 | CHANGED | rc=0 >> The nginx 80 port is not listening 192.168.64.30 | CHANGED | rc=0 >> The nginx 80 port is not listening 192.168.64.29 | CHANGED | rc=0 >> The nginx 80 port is not listening
10. 返回状态解释
绿色:成功,无更改。
黄色:成功,有更改。
红色:失败。
紫色:跳过。
蓝色:任务标题或进度。
灰色:调试信息。
白色:普通信息。
深红色:致命错误。
橙色:警告。
]# ansible nginx -m ping node1 | SUCCESS => { "ansible_facts": { "discovered_interpreter_python": "/usr/bin/python" }, "changed": false, "ping": "pong" } node2 | SUCCESS => { "ansible_facts": { "discovered_interpreter_python": "/usr/bin/python" }, "changed": false, "ping": "pong" }
常用模块
1. command模块,不支持特殊符号、正则
创建文件
]# ansible all -m command -a 'mkdir /root/1.txt' [WARNING]: Consider using the file module with state=directory rather than running 'mkdir'. 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. node1 | CHANGED | rc=0 >> node2 | CHANGED | rc=0 >>
不支持正则符合
[root@master-1 ~]# ansible nginx -m command -a "ls /usr/local/bin/*" 192.168.43.130 | FAILED | rc=2 >> ls: 无法访问/usr/local/bin/*: 没有那个文件或目录non-zero return code 192.168.43.131 | FAILED | rc=2 >> ls: 无法访问/usr/local/bin/*: 没有那个文件或目录non-zero return code [root@master-1 ~]# ansible nginx -m shell -a "ls /usr/local/bin/*" 192.168.43.130 | CHANGED | rc=0 >> /usr/local/bin/containerd /usr/local/bin/containerd-shim ... /usr/local/bin/kubelet /usr/local/bin/kube-proxy 192.168.43.131 | CHANGED | rc=0 >> /usr/local/bin/cfssl /usr/local/bin/cfssl-certinfo /usr/local/bin/cfssljson /usr/local/bin/crictl
2. shell模块,使用shell解释器执行
]# ansible all -m shell -a "id user1 || useradd user1" node2 | CHANGED | rc=0 >> id: user1: no such user node1 | CHANGED | rc=0 >> id: user1: no such user ]# ansible all -m shell -a "id user1 || useradd user1" node1 | CHANGED | rc=0 >> uid=1002(user1) gid=1002(user1) groups=1002(user1) node2 | CHANGED | rc=0 >> uid=1001(user1) gid=1001(user1) groups=1001(user1)
3. group模块
]# ansible node2 -m group -a "name=mygrp gid=2000 system=yes" # 默认state为present
]# ansible node2 -m group -a "name=mygrp state=absent" # 指定state为absent,表示remove mygrp
4. user模块
[root@master-1 ansible]# echo "123456" |openssl passwd -1 -stdin $1$6NXikaln$64e67B6Q2C0xK0fuNkVw30 [root@master-1 ansible]# ansible nginx -m user -a 'name=es uid=1006 group=es shell=/bin/bash create_home=true password="$1$6NXikaln$64e67B6Q2C0xK0fuNkVw30"' # 注意此处加密密码需要用""
5. copy模块,拷贝数据到远端
注意:copy模块只适合拷贝单个文件,copy
模块会逐个传输文件,每个文件都要建立 SSH 连接,导致大量小文件时效率低下。
默认 copy
不是增量复制,每次都会完整拷贝,即使目标已存在相同文件。
多个文件建议使用改synchronize
(基于 rsync
,速度快)
examples: - name: Copy a new "ntp.conf file into place, backing up the original if it differs from the copied version copy: src: /mine/ntp.conf dest: /etc/ntp.conf owner: root group: root mode: '0644' # 八进制,标准写法 backup: yes # 目录不存在,自动创建 [root@master-1 ansible]# ansible nginx -m copy -a "src=./test.sh dest=/tmp/ansible/ " [root@master-1 ansible]# ansible nginx -a "ls /tmp/ansible/ " 192.168.43.130 | CHANGED | rc=0 >> test.sh 192.168.43.131 | CHANGED | rc=0 >> test.sh [root@master-1 ansible]# ansible nginx -a "cat /tmp/ansible/test.sh " 192.168.43.130 | CHANGED | rc=0 >> #!/bin/bash echo "hello,ansible~" 192.168.43.131 | CHANGED | rc=0 >> #!/bin/bash echo "hello,ansible~" 拷贝文件并修改权限 ansible nginx -m copy -a "src=./test.sh dest=/tmp/ansible/test2.sh owner=nobody group=nobody mode=0644 backup=yes" [root@master-1 ansible]# ansible nginx -a "ls -l /tmp/ansible/ " 192.168.43.130 | CHANGED | rc=0 >> 总用量 8 -rw-r--r-- 1 nobody nobody 35 2月 8 11:05 test2.sh -rw-r--r-- 1 root root 35 2月 8 11:03 test.sh 192.168.43.131 | CHANGED | rc=0 >> 总用量 8 -rw-r--r-- 1 nobody nobody 35 2月 8 11:05 test2.sh -rw-r--r-- 1 root root 35 2月 8 11:03 test.sh [root@master-1 ansible]# ansible nginx -a "bash /tmp/ansible/test2.sh " 192.168.43.131 | CHANGED | rc=0 >> hello,ansible~ 192.168.43.130 | CHANGED | rc=0 >> hello,ansible~ # content写入文件 [root@master-1 playbook]# ansible nginx -m copy -a "content='echo hello world~' dest=/tmp/ansible/test5.sh" [root@master-1 playbook]# ansible nginx -a "ls -lrt /tmp/ansible/" 192.168.43.130 | CHANGED | rc=0 >> 总用量 20 -rw-r--r-- 1 nobody nobody 35 2月 8 11:05 test2.sh -rwxr-x--- 1 nobody nobody 75 2月 9 13:06 test.sh -rw-r--r-- 1 root root 79 2月 9 15:49 test3.sh -rw-r--r-- 1 root root 15 2月 9 18:00 nohup.out -rw-r--r-- 1 root root 17 2月 9 18:36 test5.sh 192.168.43.131 | CHANGED | rc=0 >> 总用量 20 -rw-r--r-- 1 nobody nobody 35 2月 8 11:05 test2.sh -rwxr-x--- 1 nobody nobody 75 2月 9 13:06 test.sh -rw-r--r-- 1 root root 79 2月 9 15:49 test3.sh -rw-r--r-- 1 root root 15 2月 9 18:00 nohup.out -rw-r--r-- 1 root root 17 2月 9 18:36 test5.sh [root@master-1 playbook]# ansible nginx -a "cat /tmp/ansible/test5.sh" 192.168.43.130 | CHANGED | rc=0 >> echo hello world~ 192.168.43.131 | CHANGED | rc=0 >> echo hello world~
6. fetch模块,只能是文件类型
拉取数据目录如果不指定,默认在当前目录
1. 创建链接文件
2. 修改属性
3. 创建目录
]# ansible node2 -m fetch -a "src=/tmp/fetch.txt dest=/tmp/fetch.txt" # 从远端复制数据到本地 node2 | CHANGED => { "changed": true, "checksum": "a8fdc205a9f19cc1c7507a60c4f01b13d11d7fd0", "dest": "/tmp/fetch.txt/node2/tmp/fetch.txt", "md5sum": "ba1f2511fc30423bdbb183fe33f3dd0f", "remote_checksum": "a8fdc205a9f19cc1c7507a60c4f01b13d11d7fd0", "remote_md5sum": null }
]# cat /tmp/fetch.txt/node2/tmp/fetch.txt 123
7. file模块,创建、修改文件/目录属性
(Choices: absent, directory, file, hard, link, touch)[Default: file]
]# ansible node2 -m file -a "group=mygrp mode=0660 path=/tmp/fetch.txt" node2 | CHANGED => { "ansible_facts": { "discovered_interpreter_python": "/usr/bin/python" }, "changed": true, "gid": 2000, "group": "mygrp", "mode": "0660", "owner": "root", "path": "/tmp/fetch.txt", "size": 4, "state": "file", "uid": 0 }
]# ll /tmp/fetch.txt -rw-rw---- 1 root mygrp 4 May 4 18:19 /tmp/fetch.txt
创建目录,如果目录不存在,会自动创建
]# ansible node2 -m file -a "group=mygrp mode=0660 path=/tmp/filemode-test state=directory" # state不指定,默认是文件类型 node2 | CHANGED => { "ansible_facts": { "discovered_interpreter_python": "/usr/bin/python" }, "changed": true, "gid": 2000, "group": "mygrp", "mode": "0660", "owner": "root", "path": "/tmp/filemode-test", "size": 6, "state": "directory", "uid": 0 }
]# ll total 1796 -rw-rw---- 1 root mygrp 4 May 4 18:19 fetch.txt drw-rw---- 2 root mygrp 6 May 4 18:55 filemode-test # 目录类型
创建目录链接
]# ansible node2 -m file -a "path=/tmp/filemode-test/fetch.txt src=/tmp/fetch.txt state=link" node2 | CHANGED => { "ansible_facts": { "discovered_interpreter_python": "/usr/bin/python" }, "changed": true, "dest": "/tmp/filemode-test/fetch.txt", "gid": 0, "group": "root", "mode": "0777", "owner": "root", "size": 14, "src": "/tmp/fetch.txt", "state": "link", "uid": 0 }
node2查看
]# ll filemode-test/ total 0 lrwxrwxrwx 1 root root 14 May 4 19:00 fetch.txt -> /tmp/fetch.txt
- name: chown redis_dir ansible.builtin.file: path: /opt/redis_cluster owner: redis group: redis state: directory recurse: yes # 递归修改
8. get_url 模块
远程服务器下载文件,并且还可以指定文件权限,属主属组
ansible nginx -m get_url -a "url=https://mirrors.tuna.tsinghua.edu.cn/elasticstack/7.x/yum/7.0.0/apm-server-7.0.0-i686.rpm dest=/tmp mode=440" [root@master-1 ansible]# ansible nginx -a "ls -l /tmp/apm*" 192.168.43.131 | CHANGED | rc=0 >> -r--r----- 1 root root 12523551 2月 9 14:39 /tmp/apm-server-7.0.0-i686.rpm 192.168.43.130 | CHANGED | rc=0 >> -r--r----- 1 root root 12523551 2月 9 14:39 /tmp/apm-server-7.0.0-i686.rpm
9. cron 模块,如果要删除 state=absent 删除即可
]# ansible all -m cron -a "minute=*/5 job='/bin/bash /tmp/test.sh' name=test" # job需要加'' # -name 注释信息 [root@master-1 ansible]# ansible nginx -m cron -a "minute=00 hour=* day=* month=* weekday=* job='/bin/bash /tmp/ansible/test.sh >/tmp/ansible/nohup.out' state=present name='hello world'" [root@master-1 ansible]# ansible nginx -a "crontab -l" 192.168.43.131 | CHANGED | rc=0 >> #Ansible: hello world 00 * * * * /bin/bash /tmp/ansible/test.sh >/tmp/ansible/nohup.out 192.168.43.130 | CHANGED | rc=0 >> 0 * * * * /opt/etcd/data/bak/etcd-bak.sh >/dev/null 2>&1 #Ansible: hello world 00 * * * * /bin/bash /tmp/ansible/test.sh >/tmp/ansible/nohup.out
10. script模块
在远程服务器执行本地脚本
ansible nginx -m script -a 'test.sh' ansible nginx -m script -a 'bash test.sh' # 不能这样写 ansible nginx -m script -a 'test.sh start' # 可以传参
11. yum模块
present:如果软件包未安装,则安装它。如果软件包已安装,则不执行任何操作。
absent:移除
latest:更新
ansible nginx -m yum -a "name=redis state=present"
12. service与systemd模块
centos7/ubuntu16之后系统之前适用于service
# 设置redis开机自启 ansible nginx -m service -a "name=redis enabled=yes" [root@master-1 ~]# ansible nginx -a "systemctl is-enabled redis" 192.168.43.131 | CHANGED | rc=0 >> enabled 192.168.43.130 | CHANGED | rc=0 >> enabled # 启动服务 ansible nginx -m service -a "name=redis state=started daemon_reload=yes" # 执行systemctl daemon-reload [root@master-1 ~]# ansible nginx -a "systemctl status redis" 192.168.43.131 | CHANGED | rc=0 >>... Active: active (running) since 日 2025-02-09 11:23:23 CST; 19s ago ...192.168.43.130 | CHANGED | rc=0 >>.. Active: active (running) since 日 2025-02-09 11:23:23 CST; 20s ago ...
13. mount模块
安装nfs服务
apt-get install nfs-kernel-server /srv/nfs 192.168.1.0/24(rw,sync,no_subtree_check) /opt/nfs *(rw,sync,no_subtree_check) sync:同步写入数据,保证数据的安全性。 no_subtree_check:不检查父目录的权限,提高效率。
挂载
[root@master-1 ansible]# ansible nginx -m mount -a "src=192.168.43.132:/opt/nfs path=/data/es fstype=nfs opts=defaults,noatime state=mounted"
查看结果
[root@master-1 ansible]# ansible nginx -a "cat /etc/fstab" 192.168.43.131 | CHANGED | rc=0 >> UUID=c448318b-13ee-4077-8dd4-953120ea14c9 / xfs defaults 0 0 UUID=632223b6-b444-4478-8261-ce08adcc0a0c /boot xfs defaults 0 0 192.168.43.131:/data/loki /opt/loki nfs defaults 0 0 UUID=eb600863-bef3-42a3-ac77-c28b5718d2b1 /kafka ext4 defaults 0 0 192.168.43.132:/opt/nfs /data/es nfs defaults,noatime 0 0
192.168.43.130 | CHANGED | rc=0 >> UUID=2782b022-83a4-438c-b94e-00c5c41e9b25 / xfs defaults 0 0 UUID=97672d99-8373-4242-b812-b0d1ae7e1703 /boot xfs defaults 0 0 192.168.43.131:/data/loki /opt/loki nfs defaults 0 0 192.168.43.132:/opt/nfs /data/es nfs defaults,noatime 0 0 [root@master-1 ansible]# ansible nginx -a "df -hT |grep 132" 192.168.43.130 | CHANGED | rc=0 >> 192.168.43.132:/opt/nfs nfs4 17G 11G 7.0G 60% /data/es 192.168.43.131 | CHANGED | rc=0 >> 192.168.43.132:/opt/nfs nfs4 17G 11G 7.0G 60% /data/es
14. setup
获取主机相关信息
[root@master-1 ansible]# ansible nginx -m setup -a "filter=ansible_default_ipv4" # 过滤 192.168.43.131 | SUCCESS => { "ansible_facts": { "ansible_default_ipv4": { "address": "192.168.43.131", "alias": "ens33", "broadcast": "192.168.43.255", "gateway": "192.168.43.1", "interface": "ens33", "macaddress": "00:0c:29:6e:2a:60", "mtu": 1500, "netmask": "255.255.255.0", "network": "192.168.43.0", "type": "ether" }, "discovered_interpreter_python": "/usr/bin/python" }, "changed": false } 192.168.43.130 | SUCCESS => { "ansible_facts": { "ansible_default_ipv4": { "address": "192.168.43.130", "alias": "ens33", "broadcast": "192.168.43.255", "gateway": "192.168.43.1", "interface": "ens33", "macaddress": "00:0c:29:38:22:1d", "mtu": 1500, "netmask": "255.255.255.0", "network": "192.168.43.0", "type": "ether" }, "discovered_interpreter_python": "/usr/bin/python" }, "changed": false } ansible nginx -m setup -a "filter=ansible_*_ipv4" # 支持正则
15. archive
在远程服务器进行打包
exclude_path:排除,可能不好用,如果有这个需求,使用shell执行 - format The type of compression to use. Support for xz was added in Ansible 2.5. (Choices: bz2, gz, tar, xz, zip)[Default: gz] type: str path:要进行压缩的文件或目录 dest:存储的路径 remove: 压缩完成后是否进行移除源文件,默认false archive: path: - /path/to/foo/ - /path/wong/foo dest: /path/file.tar.bz2 exclude_path: - /path/to/foo/bar - /path/to/foo/baz format: bz2 ansible nginx -m archive -a "path='/tmp/ansible/* /var/log/*' dest=/tmp/ansible-archive.tar.gz exclude_path=['/var/log/message', '/var/log/pods/'] format=gz"
16. unarchive解压
remote_src: yes 使用目标主机的文件解压,不进行传输
src: foo.tgz → **本地(控制节点)**的 foo.tgz 文件 dest: /var/lib/foo → 解压到远程主机的 /var/lib/foo 默认会将 foo.tgz 复制到远程机器 再解压 - name: Extract foo.tgz into /var/lib/foo unarchive: src: foo.tgz dest: /var/lib/foo src: /tmp/foo.zip → 远程机器上的 /tmp/foo.zip remote_src: yes → 不从控制节点传输文件,而是直接在远程机器解压 - name: Unarchive a file that is already on the remote machine unarchive: src: /tmp/foo.zip dest: /usr/local/bin remote_src: yes 从 URL 下载 并解压 → 方式 3 - name: Unarchive a file that needs to be downloaded (added in 2.0) unarchive: src: https://example.com/example.zip dest: /usr/local/bin remote_src: yes
17. fetch 模块
将远程主机的文件拉取到ansible本地
如果抓取多个主机的文件,ansible 会以主机ip或名称 创建对应目录存放拉取到的文件
- flat 允许您覆盖将主机名/路径/文件附加到目标的默认行为。 如果“dest”以“/”结尾,它将使用源文件的基本名称,类似于复制模块。 如果使用单个主机,或者检索每个主机唯一命名的文件,这将很有用。 如果使用具有相同文件名的多个主机,则每个主机的文件将被覆盖。 [默认值:False] 类型:bool version_added:1.2
[root@master-1 ansible]# ansible nginx -m fetch -a "src=/tmp/ansible/test5.sh dest=/opt/ansible" 192.168.43.130 | CHANGED => { "changed": true, "checksum": "127716fac5fb00a750c931183a4ca99685105b7c", "dest": "/opt/ansible/192.168.43.130/tmp/ansible/test5.sh", "md5sum": "065bb662c5d713d38e5b0796edf4ebca", "remote_checksum": "127716fac5fb00a750c931183a4ca99685105b7c", "remote_md5sum": null } 192.168.43.131 | CHANGED => { "changed": true, "checksum": "127716fac5fb00a750c931183a4ca99685105b7c", "dest": "/opt/ansible/192.168.43.131/tmp/ansible/test5.sh", "md5sum": "065bb662c5d713d38e5b0796edf4ebca", "remote_checksum": "127716fac5fb00a750c931183a4ca99685105b7c", "remote_md5sum": null } [root@master-1 ansible]# ll 总用量 4 drwxr-xr-x 3 root root 17 2月 11 10:00 192.168.43.130 drwxr-xr-x 3 root root 17 2月 11 10:00 192.168.43.131 drwxr-xr-x 3 root root 53 2月 9 21:03 playbook -rwxr-xr-x 1 root root 199 2月 10 14:52 test.sh [root@master-1 ansible]# ll 192.168.43.130 总用量 0 drwxr-xr-x 3 root root 21 2月 11 10:00 tmp [root@master-1 ansible]# ll 192.168.43.130/tmp/ansible/test5.sh -rw-r--r-- 1 root root 17 2月 11 10:00 192.168.43.130/tmp/ansible/test5.sh [root@master-1 ansible]# ll 192.168.43.131/tmp/ansible/test5.sh -rw-r--r-- 1 root root 17 2月 11 10:00 192.168.43.131/tmp/ansible/test5.sh
18. Lineinfile 模块
用于 添加、修改或删除 特定的一行,主要适用于配置文件的管理。
主要特点- 匹配行:基于
regexp
(正则表达式)或line
直接匹配。 - 修改或追加:如果匹配行存在,则修改;如果不存在,可以追加(
insertbefore
/insertafter
)。 - 确保幂等性:如果目标行已经符合要求,则不会重复修改。
(1)修改或添加一行
- name: 确保配置文件中包含 `PermitRootLogin yes` ansible.builtin.lineinfile: path: /etc/ssh/sshd_config regexp: '^PermitRootLogin' line: 'PermitRootLogin yes'
解释:
- 如果
/etc/ssh/sshd_config
中 已经有PermitRootLogin xxx
,它会修改该行。- 如果 没有 这行,它会自动添加
PermitRootLogin yes
。
(2)在某行之前或之后插入
- name: 在 `root ALL=(ALL) ALL` 之后插入 `admin ALL=(ALL) ALL` ansible.builtin.lineinfile: path: /etc/sudoers insertafter: '^root ALL=\(ALL\) ALL' line: 'admin ALL=(ALL) ALL'
(3)删除一行
- name: 删除 `PermitRootLogin no` 这一行 ansible.builtin.lineinfile: path: /etc/ssh/sshd_config regexp: '^PermitRootLogin no' state: absent
19. Replace模块
用于 替换文件中所有匹配的内容,适用于大范围修改,不局限于单行。
主要特点- 基于正则匹配 进行替换。
- 替换所有匹配项(不像
lineinfile
只作用于一行)。 - 适用于批量修改文本。
(1)修改所有匹配的文本
- name: 将 `/var/www/html` 替换为 `/srv/web` ansible.builtin.replace: path: /etc/apache2/sites-available/000-default.conf regexp: '/var/www/html' replace: '/srv/web'
解释:
- 匹配
/var/www/html
,并替换为/srv/web
。- 所有匹配的地方都会被替换(不是仅限于某一行)。
(2)删除所有匹配的行
- name: 删除所有 `#DebugMode=on`(注释的调试模式) ansible.builtin.replace: path: /etc/app/config.ini regexp: '^#DebugMode=on' replace: ''
解释:
- 匹配
#DebugMode=on
并替换为空,即删除该行。
(3). lineinfile
vs replace
适用场景
模块 | 适用场景 | 优点 | 缺点 |
---|---|---|---|
lineinfile |
确保某个配置行存在或修改 | 幂等性强,适用于配置管理 | 不能批量替换 |
replace |
批量替换文本(所有匹配项) | 适用于大规模文本替换 | 非幂等,需要小心使用 |
4. 选择使用哪一个?
- 如果你要修改或添加 特定的一行,使用
lineinfile
。✅ - 如果你要替换 所有匹配的内容,使用
replace
。✅ - 如果你要删除某行,两者都可以:
lineinfile
适合 删除特定的某行。replace
适合 删除所有匹配的内容。
20. git 模块
指定版本拉取
- git: repo: 'https://foosball.example.org/path/to/repo.git' dest: /srv/checkout version: release-0.22
对文档进行压缩
# Allowed archive formats ["zip", "tar.gz", "tar", "tgz"] - git: repo: https://github.com/ansible/ansible-examples.git dest: /src/ansible-examples archive: /tmp/ansible-examples.zip
21. blockinfile模块
blockinfile
是 Ansible 用于管理 配置文件中的一段文本块 的模块,适用于 追加、更新或删除 目标文件中的多行配
参数 | 说明 | 默认值 |
---|---|---|
path |
目标文件路径 | 必填 |
block |
需要插入的内容 | 必填 |
marker |
标记文本块的起始和结束 | # {mark} ANSIBLE MANAGED BLOCK |
insertbefore |
在匹配行前插入 | EOF (默认末尾) |
insertafter |
在匹配行后插入 | EOF (默认末尾) |
create |
文件不存在时是否创建 | no |
backup |
修改前是否备份 | no |
1. 在 /etc/profile
添加 Java 环境变量
- name: 添加或更新文件中的文本块
ansible.builtin.blockinfile:
path: /etc/example.conf # 目标文件路径
block: |
[example]
key1 = value1
key2 = value2
marker: "# {mark} ANSIBLE MANAGED BLOCK"
再次运行 Playbook 时,不会重复添加,只会保持已有内容。
参数解释:
marker: "# {mark} ANSIBLE MANAGED BLOCK"
生成注释
# BEGIN ANSIBLE MANAGED BLOCK - JAVA export JAVA_HOME=/opt/jdk1.8.0_341 export CLASSPATH=.:/opt/jdk1.8.0_341/jre/lib/rt.jar:/opt/jdk1.8.0_341/lib/dt.jar:/opt/jdk1.8.0_341/lib/tools.jar export PATH=/opt/jdk1.8.0_341/bin:$PATH # END ANSIBLE MANAGED BLOCK - JAVA
2. 在指定行前插入内容
- name: 在匹配的行 **前** 插入文本块 ansible.builtin.blockinfile: path: /etc/ssh/sshd_config insertbefore: "^#PermitRootLogin" block: | PermitRootLogin no
3. 在指定行后插入内容
- name: 在匹配的行 **后** 插入文本块 ansible.builtin.blockinfile: path: /etc/sysctl.conf insertafter: "^net.ipv4.ip_forward" block: | net.ipv4.conf.all.rp_filter = 1
4. 确保文件存在(如果不存在则创建)
- name: 确保 `/etc/myconfig.conf` 存在并写入配置 ansible.builtin.blockinfile: path: /etc/myconfig.conf create: yes block: | [myconfig] option1 = value1 option2 = value2
如果 /etc/myconfig.conf
不存在,则 自动创建并写入 配置。
5. 修改前备份原文件
- name: 修改 `/etc/hosts` 并备份 ansible.builtin.blockinfile: path: /etc/hosts backup: yes block: | 192.168.1.100 myserver.local
6. 删除 blockinfile
插入的内容
- name: 从 `/etc/profile` 删除 Java 配置 ansible.builtin.blockinfile: path: /etc/profile marker: "# {mark} ANSIBLE MANAGED BLOCK - JAVA" state: absent
只要 marker
里匹配 # {mark} ANSIBLE MANAGED BLOCK - JAVA
的内容,整块删除。
blockinfile
VS lineinfile
特性 | blockinfile | lineinfile |
---|---|---|
作用 | 管理多行文本 | 仅管理单行 |
marker |
标记插入内容 | 不适用 |
适用于 | 配置文件的整个区块 | 添加/修改单行 |
22. sysctl模块
- sysctl: name: vm.swappiness
# 必须使用单引号'' value: '5' state: present
# [Default: /etc/sysctl.conf] sysctl_file: /tmp/test_sysctl.conf
# 相当于/sbin/sysctl -p reload: yes
23. wait_for模块
wait_for
模块用于等待某个条件满足,比如等待端口开放、等待文件存在、等待指定的超时时间等。这个模块在部署过程中非常有用,尤其是在等待远程主机的某些服务就绪时。
state
state: started 仅指端口监听,不会对端口状态进行检测
检查端口时:
“started”将确保端口处于打开状态
“stopped”将检查端口是否已关闭
“drained”将检查活动连接。
检查文件或搜索字符串时,“present”或“started”将确保文件或字符串在继续之前存在,“absent”将检查文件是否存在或被删除。
检查端口
- name: 等待 Elasticsearch 端口 9200 可用 ansible.builtin.wait_for: #host: "{{ ansible_default_ipv4.address }}" host: "{{ ansible_host }}" port: "{{ listen_port }}" # 每 5 秒检查一次端口状态 delay: 5 # 总的等待超时时间(秒)。如果在这个时间内端口没有变得可用,任务将失败。 timeout: 60 state: started register: es_ready changed_when: false
等待文件存在及指定内容
- name: 等待文件 /tmp/ready.txt 存在 wait_for: path: /tmp/ready.txt search_regex: "^node.name" timeout: 60 delay: 5
检查活动链接
- name: Wait for port 8000 of any IP to close active connections, ignoring connections for specified hosts wait_for: host: 0.0.0.0 port: 8000 state: drained exclude_hosts: 10.2.1.2,10.2.1.3
24. uri模块
uri 模块用于发送 HTTP 请求,并可以检查返回的状态码、响应内容等。它常用于验证 Web 服务是否正常运行、发送 API 请求等。
对响应值进行包含测试
- name: Check that a page returns a status 200 and fail if the word AWESOME is not in the page contents uri: url: http://www.example.com return_content: yes register: this failed_when: "'AWESOME' not in this.content"
对访问端口的http状态检测
- name: 等待 HTTP 服务返回 200 uri: url: http://localhost:8080 status_code: 200 # 将uri模块执行的结果注册到变量result中 register: result # 循环条件,表示任务会一直重复执行 until: result.status == 200 # 重试10次 retries: 10 # 间隔5s delay: 5
发送post请求
- name: 发送 JSON 数据 uri: url: https://example.com/api method: POST body: '{"name": "Ansible", "type": "automation"}' body_format: json headers: Content-Type: "application/json" status_code: 201 url_username: admin url_password: secret force_basic_auth: yes # 默认30s timeout: 30 # 对于https请求,忽略证书校验 validate_certs: no
24. stat模块
stat 模块用于 检查远程主机上的文件或目录 是否存在,并获取 权限、大小、所有者等信息。
类似 Linux stat 命令,可用于 判断文件是否存在,从而决定是否执行后续任务。
注意: stat 模块无法直接读取文件内容,它只能获取文件的元数据(如是否存在、大小、权限、所有者等)
stat
常见参数
参数 | 作用 |
---|---|
path |
必填,要检查的文件/目录路径 |
get_md5 |
是否获取文件 MD5 校验值(默认 false ) |
get_checksum |
是否获取 SHA1 校验值(默认 true ) |
checksum_algorithm |
指定校验算法,如 sha256 |
get_mime |
获取文件 MIME 类型 |
stat
返回信息
stat
返回一个字典,包含以下关键值:
返回字段 | 说明 |
---|---|
exists |
是否存在 (true / false ) |
isdir |
是否是目录 |
isreg |
是否是普通文件 |
size |
文件大小(字节) |
mode |
权限(八进制,如 0644 ) |
pw_name |
所有者(用户名) |
gr_name |
所属组 |
checksum |
SHA1 校验值 |
md5 |
MD5 值(需要 get_md5: true ) |
mime_type |
文件 MIME 类型 |
1. 判断文件是否存在
- name: 检查 nginx 配置文件 ansible.builtin.stat: path: /etc/nginx/nginx.conf register: nginx_config - name: 只有文件存在时才执行 ansible.builtin.debug: msg: "nginx 配置文件存在" when: nginx_config.stat.exists # 固定格式为xxx.stat.xxx
2. 判断目录是否存在
- name: 检查 /var/log 目录 ansible.builtin.stat: path: /var/log register: log_dir - name: 目录存在时执行 debug: msg: "日志目录存在" when: log_dir.stat.isdir
3. 判断文件大小
- name: 检查文件大小 ansible.builtin.stat: path: /var/log/messages register: log_file - name: 如果日志文件超过 10MB,则删除 ansible.builtin.file: path: /var/log/messages state: absent when: log_file.stat.exists and log_file.stat.size > 10485760
📌 文件大于 10MB(10 * 1024 * 1024
字节)才删除。
4. 获取文件权限
- name: 检查 /etc/passwd 权限 ansible.builtin.stat: path: /etc/passwd register: passwd_file - name: 输出文件权限 debug: msg: "文件权限是 {{ passwd_file.stat.mode }}"
5. 判断二进制服务是否运行
- name: 检查 nginx.pid 是否存在 ansible.builtin.stat: path: /var/run/nginx.pid register: nginx_pid - name: 启动 nginx(如果未运行) ansible.builtin.shell: "/usr/sbin/nginx" when: not nginx_pid.stat.exists
📌 基于 pid
文件判断服务是否运行,不存在则启动。
6. 检测文件 MD5
- name: 获取文件 MD5 ansible.builtin.stat: path: /tmp/testfile get_md5: true register: file_stat - name: 输出 MD5 值 debug: msg: "文件 MD5 是 {{ file_stat.stat.md5 }}"
扩展:
对文件内容进行判断
方法 1:grep
+ shell
(推荐)
- name: 检查 nginx 配置文件中是否有 "server_name" ansible.builtin.shell: "grep 'server_name' /etc/nginx/nginx.conf" register: nginx_check ignore_errors: yes # 如果 grep 失败(找不到字符串),不让 Playbook 终止 - name: 输出检查结果 debug: msg: "找到 server_name 配置!" when: nginx_check.rc == 0
方法 2:lineinfile
查找字符串
- name: 检查 nginx.conf 是否包含 "server_name" ansible.builtin.lineinfile: path: /etc/nginx/nginx.conf line: "server_name example.com;" state: present check_mode: yes # 仅检查,不修改 register: check_nginx - name: 输出文件是否包含该行 debug: msg: "文件中已包含 server_name!" when: check_nginx.found > 0
方法 3:读取整个文件内容并判断
原理:
slurp
读取整个文件,并以 Base64 编码返回。b64decode
进行解码,然后使用 Jinja2 过滤器查找字符串。
- name: 读取 /etc/nginx/nginx.conf 内容 ansible.builtin.slurp: path: /etc/nginx/nginx.conf register: file_content - name: 判断文件内容是否包含 "server_name" debug: msg: "找到 server_name 配置" when: "'server_name' in file_content.content | b64decode"
25. synchronize模块
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 使用C#创建一个MCP客户端
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 按钮权限的设计及实现