ansible简介
1. 概述
官方简介
http://www.ansible.com.cn/docs/
Ansible命令选项
参数 | 英文释义 | 中文释义 |
---|---|---|
-i | inventory list | 指定主机清单 |
-m | module | 指定模块 |
-a | action | 指定动作(一般是模块中的参数) |
环境初始化
#windows不能作为主控设备
#(不建议)安装centos7 默认ansible-2.9.27版本(可能会造成下面部分命令参数不可用)
yum install epel-release -y
yum install ansible -y
#(建议)安装最新ansible-4.10 python3.x
pip3 install ansible
#创建默认配置文件
mkdir -p /etc/ansible
ansible-config init --disabled > /etc/ansible/ansible.cfg
#验证
python3 -m pip show ansible
which ansible
PS:
ERROR! Unexpected Exception, this is probably a bug: (cryptography 0.8.2 (/usr/lib64/python2.7/site-packages), Requirement.parse(‘cryptography>=1.1’))
#cryptography包冲突
rpm -qa |grep python-crypto
yum -y remove python-cryptography
pip3 install ansible
ERROR! Unexpected Exception, this is probably a bug: module ‘ansible.constants’ has no attribute ‘CONTROLLER_PYTHON_WARNING’
#检查环境变量里的ansible是否齐全
whereis ansible
ll /usr/local/bin/ansible*
ll /usr/bin/ansible*
#一般在 $PYTHON_HOME/bin/ansible
#旧ansible删干净
rm -rf /usr/local/bin/ansible*
#重建ansible软链接
ln /opt/env/python38/bin/ansible /usr/local/bin/ansible
#或清空所有ansible重新安装
文档
https://blog.csdn.net/sj349781478/article/details/105755615/
https://docs.ansible.com/
https://docs.ansible.com/ansible/latest/collections/index.html
https://docs.ansible.com/ansible/latest/collections/ansible/builtin/index.html#modules
ansible-doc -s 模块名
Bili教程
使用前的准备工作
0. 新建初始化目录
mkdir -p /opt/ansible
1. 修改全局配置
#查看ansible.cfg路径
#ansible --version | grep "config file" | awk -F " = " '{print $2}'
#为None说明文件不存在
mkdir -p /etc/ansible
vi /etc/ansible/ansible.cfg
[defaults]
#设置是否检查SSH主机的密钥,关闭后第一次连接不会提示配置实例
host_key_checking = False
#主机列表配置文件
inventory = /opt/ansible/hosts
# 指定ansible命令执行的用户,默认使用当前用户
remote_user = root
#库文件存放目录
#library = /usr/share/my_modules/
#临时py文件存放在远程主机目录
#remote_tmp = ~/.ansible/tmp
#本机的临时执行目录
#local_tmp = ~/.ansible/tmp
#默认并发数
#forks = 5
#默认sudo用户
#sudo_user = root
#每次执行是否询问sudo的ssh密码
#ask_sudo_pass = True
#每次执行是否询问ssh密码
#ask_pass = True
#远程主机端口
#remote_port = 22
#ansible日志目录
#log_path = /var/log/ansible.log
#指定通信机制
#transport = smart
# 角色配置路径
#roles_path = /etc/ansible/roles
# ssh连接超时
#timeout = 10
# ansible命令默认执行模块
#module_name = command
#普通用户提权操作
#[privilege_escalation]
#become=True
#become_method=sudo
#become_user=root
#become_ask_pass=False
简洁配置
echo -e "[defaults]
inventory = /opt/ansible/hosts
host_key_checking = False
remote_user = root">/etc/ansible/ansible.cfg
2. 修改主机清单
vi /opt/ansible/hosts
[gp-master]
192.168.1.183
#设置所有主机的配置
[all:vars]
ansible_ssh_port=22
ansible_ssh_user=root
ansible_ssh_pass=6
2. 主机清单
2.1 使用前的优化
#优化ssh速度
vi /etc/ssh/sshd_config
UseDNS no
GSSAPIAuthentication no
GSSAPICleanupcredentials no
2.1 配置主机清单
vi /opt/ansible/hosts
#组
[gp]
192.168.1.170
192.168.1.171
192.168.1.172
192.168.1.173
#上面4台设备等同于=>
[gp]
192.168.1.17[0:3]
#子组
[gp-master]
192.168.1.170
[gp-datenode01]
192.168.1.171
[gp-datenode02]
192.168.1.172
[gp-datenode03]
192.168.1.173
#建立gp子组
[gp:children]
gp-master
gp-datenode01
gp-datenode02
gp-datenode03
#带配置项
[master]
192.168.1.170 ansible_ssh_port=22 ansible_ssh_user=root ansible_ssh_pass=6 hostname=gp_master
#对组批量配置
#设置gp-master组的所有配置
[gp-master:vars]
ansible_ssh_port=22
ansible_ssh_user=root
ansible_ssh_pass=6
hostname=gp_master
#all 表示所有
[all:vars]
3. 模块
ansible -m 模块名 -a '参数'
不带参数默认使用command
https://docs.ansible.com/ansible/latest/collections/index_module.html
https://docs.ansible.com/ansible/latest/collections/index_module.html#ansible-builtin
https://docs.ansible.com/ansible/latest/collections/index_module.html#ansible-posix
1. 执行脚本
script 脚本执行器
https://docs.ansible.com/ansible/latest/collections/ansible/builtin/script_module.html#ansible-collections-ansible-builtin-script-module
命令 | 参数 | 说明 |
---|---|---|
cmd | string | 要运行的本地脚本的路径,后跟可选参数 |
chdir | path | 执行命令前请进入该目录(cd path) |
executable | string | 用于调用脚本的可执行文件的名称或路径(默认为bash),可自行定义为python等 |
creates | path | 如果匹配的文件已经存在,则不会运行此步骤 |
removes | path | 如果存在匹配的文件,则运行此步骤 |
ansible test -m script -a "xxx.sh"
- name: Run a script with arguments (free form)
ansible.builtin.script: /some/local/script.sh --some-argument 1234
- name: Run a script with arguments (using 'cmd' parameter)
ansible.builtin.script:
cmd: /some/local/script.sh --some-argument 1234
- name: Run a script only if file.txt does not exist on the remote node
ansible.builtin.script: /some/local/create_file.sh --some-argument 1234
args:
creates: /the/created/file.txt
command 命令执行器
不支持通配符,管道符,变量(等很多都不能用)!
https://docs.ansible.com/ansible/latest/collections/ansible/builtin/command_module.html#ansible-builtin-command-module-execute-commands-on-targets
命令 | 参数 | 说明 |
---|---|---|
cmd | string | 要运行的命令后面跟着可选参数 |
chdir | path | 执行命令前请进入该目录(cd path) |
executable | string | 修改执行命令的shell,需要一个可执行文件的绝对路径 |
argv | list | 以列表而不是字符串的形式传递命令 |
creates | path | 如果匹配的文件已经存在,则不会运行此步骤 |
removes | path | 如果存在匹配的文件,则运行此步骤 |
stdin | string | 将命令的 stdin 直接设置为指定的值 |
stdin_add_newline | boolean | 是否向标准输入数据追加换行符 |
strip_empty_ends | boolean | 从result中stdout/stderr的末尾删除空行 |
ansible test -m command -a "ls /tmp/"
ansible test -m command -a "cat /tmp/hello.txt"
- name: Return motd to registered var
ansible.builtin.command: cat /etc/motd
register: mymotd
# free-form (string) arguments, all arguments on one line
- name: Run command if /path/to/database does not exist (without 'args')
ansible.builtin.command: /usr/bin/make_database.sh db_user db_name creates=/path/to/database
#'args'是一个任务关键字,与模块在同一级别传递
- name: Change the working directory to somedir/ and run the command as db_owner if /path/to/database does not exist
ansible.builtin.command: /usr/bin/make_database.sh db_user db_name
become: yes
become_user: db_owner
args:
chdir: somedir/
creates: /path/to/database
- name: Safely use templated variable to run command. Always use the quote filter to avoid injection issues
ansible.builtin.command: cat {{ myfile|quote }}
register: myoutput
- name: Run command if /path/to/database does not exist (with 'cmd' parameter)
ansible.builtin.command:
cmd: /usr/bin/make_database.sh db_user db_name
creates: /path/to/database
修改默认模块为shell
vi /etc/ansible/ansible.cfg
module_name = shell
shell 命令执行器
(使用”)通配符,管道符,变量 基本上都支持
https://docs.ansible.com/ansible/latest/collections/ansible/builtin/shell_module.html#ansible-builtin-shell-module-execute-shell-commands-on-targets
命令 | 参数 | 说明 |
---|---|---|
cmd | string | 要运行的命令后面跟着可选参数 |
chdir | path | 执行命令前请进入该目录(cd path) |
executable | string | 修改执行命令的shell,需要一个可执行文件的绝对路径 |
creates | path | 如果匹配的文件已经存在,则不会运行此步骤 |
removes | path | 如果存在匹配的文件,则运行此步骤 |
stdin | string | 将命令的 stdin 直接设置为指定的值 |
stdin_add_newline | boolean | 是否向标准输入数据追加换行符 |
ansible test -m shell -a 'ls /tmp/'
#查看端口监听
ansible test -m shell -a "ss -ntl | grep '\*:\*' | awk '{print \$4}'"
#查看端口监听程序
ansible test -m shell -a "ss -ntlp | grep ':6379' | awk '{print \$6}'"
#杀进程
ansible test -m shell -a "killall nginx"
ansible test -m shell -a 'cat /tmp/hello.txt'
- name: Execute the command in remote shell; stdout goes to the specified file on the remote
ansible.builtin.shell: somescript.sh >> somelog.txt
- name: This command will change the working directory to somedir/
ansible.builtin.shell:
cmd: ls -l | grep log
chdir: somedir/
- name: '查看进程状态' # 对每个管道错误都对外抛出
shell:
cmd: 'set -o pipefail && ps aux | grep fdfs_ | grep -v grep'
expect 带交互的命令执行器
执行命令并响应设定交互
https://docs.ansible.com/ansible/latest/collections/ansible/builtin/expect_module.html#ansible-builtin-expect-module-executes-a-command-and-responds-to-prompts
命令 | 参数 | 说明 |
---|---|---|
command | string | 要运行的命令 |
responses | dictionary | 期望的字符串/正则表达式和响应的字符串的映射. 如果响应是一个列表,连续的匹配将返回连续的响应 |
echo | boolean | 是否回显你的响应字符串 false-(默认) |
timeout | integer | 等待预期字符串的时间(以秒为单位) 使用 null 禁用超时 30-(默认) |
chdir | path | 执行命令前请进入该目录(cd path) |
creates | path | 如果文件已经存在,则不会运行此步骤 |
removes | path | 如果文件不存在,则不会运行此步骤 |
ansible test -m expect -a 'command="passwd gpadmin" responses={".*密码":"123456"} echo=true'
- name: Case insensitive password string match
ansible.builtin.expect:
command: passwd username
responses:
(?i)password: "MySekretPassword"
# you don't want to show passwords in your logs
no_log: true
- name: Generic question with multiple different responses
ansible.builtin.expect:
command: /path/to/custom/command
responses:
Question:
- response1
- response2
- response3
PS
FAILED! => {
"changed": false,
"msg": "Failed to import the required Python library (pexpect) on gp-datanode01's Python /usr/bin/python3. Please read the module documentation and install it in the appropriate location. If the required library is installed, but Ansible is using the wrong Python interpreter, please consult the documentation on ansible_python_interpreter"
}#Python library (pexpect) => pexpect库不存在 pip install pexpect #指定py版本 ansible_python_interpreter=/usr/bin/python3 #或直接替换原始(系统会崩) ln -sf /usr/bin/python3.6 /usr/bin/python3 ln -sf /usr/bin/pip-3.6 /usr/bin/pip3 ln -sf /usr/bin/python3 /usr/bin/python ln -sf /usr/bin/pip3 /usr/bin/pip ln -sf /usr/bin/python3 /usr/local/bin/python ln -sf /usr/bin/pip3 /usr/local/bin/pip
template Jinja2模板填充分发
参数同copy
https://docs.ansible.com/ansible/latest/collections/ansible/builtin/template_module.html#ansible-builtin-template-module-template-a-file-out-to-a-target-host
命令 | 参数 | 说明 |
---|---|---|
src | path | Jinja2格式模板的路径(文件必须使用utf-8编码) |
dest | path | 在远程计算机上呈现模板的位置 |
force | boolean | true-文件不一致时替换远程文件(默认) false-仅当目标不存在时才传输文件 |
mode | 0777 or u+rwx or u=rw,g=r,o=r |
目标文件或目录的权限 |
newline_sequence | string | \n (默认) |
output_encoding | string | 重写用于写入dest定义的模板文件的编码,默认为utf-8,可以使用python支持的任何编码 |
owner | string | 应该拥有文件系统对象的用户名(chown) |
group | string | 应该拥有文件系统对象的组的名称(chown) |
backup | boolean | 备份 |
ansible test -m template -a 'src=/etc/hostname dest=/tmp/hostname'
ansible test -m template -a 'src=/opt/ansible/hello.j2 dest=/opt/ansible/hello.txt' -e "helloworld=QL"
- name: Template a file to /etc/file.conf
ansible.builtin.template:
src: /mytemplates/foo.j2
dest: /etc/file.conf
owner: bin
group: wheel
mode: '0644'
- name: Template a file, using symbolic modes (equivalent to 0644)
ansible.builtin.template:
src: /mytemplates/foo.j2
dest: /etc/file.conf
owner: bin
group: wheel
mode: u=rw,g=r,o=r
模板文件
https://jinja.palletsprojects.com/en/latest/
HelloWorld : {{helloworld}}
2. 文件/文本操作
replace 文本替换
https://docs.ansible.com/ansible/latest/collections/ansible/builtin/replace_module.html#ansible-builtin-replace-module-replace-all-instances-of-a-particular-string-in-a-file-using-a-back-referenced-regular-expression
命令 | 参数 | 说明 |
---|---|---|
path | path | 要修改的文件 |
replace | string | 替换regexp的字符串匹配. 可以包含反向引用,如果regexp匹配,将使用regexp捕获组展开. 如果未设置,则完全删除匹配项. 反向引用可以模糊地像\1那样使用,也可以明确地像\g<1>那样使用 |
regexp | string | 要在文件内容中查找的(Python)正则表达式 |
after | string | 如果指定了,只有匹配之后的内容才会被替换/删除. 可与before 结合使用 |
before | string | 如果指定,则只替换/删除匹配之前的内容. 可与after 配合使用 |
encoding | string | 编码 utf-8 -(默认) |
mode | 0777 or u+rwx or u=rw,g=r,o=r |
目标文件或目录的权限 |
create | boolean | 与 state = current 一起使用 true-创建尚不存在的文件 false-(默认) |
owner | string | 应该拥有文件系统对象的用户名(chown) |
group | string | 应该拥有文件系统对象的组的名称(chown) |
backup | boolean | 备份 |
#模糊匹配替换(最后一个匹配位置),找不到默认插入末尾
ansible test -m replace -a 'path=/etc/hosts replace="192.168.1.181 gp-datanode03" regexp="192\.168\.1\.183.*" search_string="192.168.1.181"'
- name: 替换183为QL
replace:
path: /opt/ansible/hostsssss
regexp: "192\\.168\\.1\\.183.*"
replace: QL
- name: 替换
replace:
path: /opt/ansible/hostsssss
regexp: "QL"
replace: "192.168.1.183 gp-master"
lineinfile 文本操作
https://docs.ansible.com/ansible/latest/collections/ansible/builtin/lineinfile_module.html#ansible-builtin-lineinfile-module-manage-lines-in-text-files
命令 | 参数 | 说明 |
---|---|---|
path | path | 要修改的文件 |
line | boolean | 要插入/替换到文件中的行 |
state | absent present |
absent-删除(所有匹配) present-添加/替换(默认) |
search_string | string | 要在文件的每一行中查找的字符串. 对于state=present ,找到则替换该行(只替换找到的最后一行) 对于state=absent ,如果找到则删除该行. 如果文本表达式不匹配,那么这一行将被添加到文件中(根据insertafter 或insertbefore 的设置). 不能与regexp 一起使用 |
regexp | string | 要在文件的每一行中查找的(Python)正则表达式. 对于state=present ,找到则替换(只替换找到的最后一行) 对于state=absent ,如果找到则删除该行. 如果正则表达式不匹配,则该行将被添加到文件中(根据insertbefore 或insertafter 的设置) https://docs.python.org/zh-cn/3/library/re.html |
insertbefore | string | 如果正则表达式同时传递给regexp和insertbefore,则只有在没有找到与regexp匹配的情况下才使用insertbefore. 不能与insertafter 一起使用 |
insertafter | string | 如果正则表达式同时传递给regexp和insertafter,则只有在没有找到与regexp匹配的情况下才使用insertafter. 不能与insertbefore 一起使用 |
firstmatch | boolean | 与insertafter 或insertbefore 一起使用 true-第一个匹配 false-最后一个匹配 |
mode | 0777 or u+rwx or u=rw,g=r,o=r |
目标文件或目录的权限 |
create | boolean | 与 state = current 一起使用 true-创建尚不存在的文件 false-(默认) |
owner | string | 应该拥有文件系统对象的用户名(chown) |
group | string | 应该拥有文件系统对象的组的名称(chown) |
backup | boolean | 备份 |
#模糊匹配替换(最后一个匹配位置),找不到默认插入末尾
ansible test -m lineinfile -a 'path=/etc/hosts line="192.168.1.181 gp-datanode03" state=present search_string="192.168.1.181"'
#正则匹配替换(最后一个匹配位置),找不到默认插入末尾
ansible test -m lineinfile -a 'path=/etc/hosts line="192.168.1.181 gp-datanode03" state=present regexp="192\.168\.1\.181.*"'
#模糊匹配删除(删除所有),找不到不操作
ansible test -m lineinfile -a 'path=/etc/hosts search_string="192.168.1.181" state=absent'
#模糊匹配(最后一个匹配位置),后置插入
ansible test -m lineinfile -a 'path=/etc/hosts line="192.168.1.184 QL" state=present insertafter="192.168.1.183"'
#模糊匹配(最后一个匹配位置),前置插入
ansible test -m lineinfile -a 'path=/etc/hosts line="192.168.1.184 QL" state=present insertbefore="192.168.1.183"'
- name: Ensure SELinux is set to enforcing mode
ansible.builtin.lineinfile:
path: /etc/selinux/config
regexp: '^SELINUX='
line: SELINUX=enforcing
- name: Make sure group wheel is not in the sudoers configuration
ansible.builtin.lineinfile:
path: /etc/sudoers
state: absent
regexp: '^%wheel'
- name: Replace a localhost entry with our own
ansible.builtin.lineinfile:
path: /etc/hosts
regexp: '^127\.0\.0\.1'
line: 127.0.0.1 localhost
owner: root
group: root
mode: '0644'
- name: Replace a localhost entry searching for a literal string to avoid escaping
ansible.builtin.lineinfile:
path: /etc/hosts
search_string: '127.0.0.1'
line: 127.0.0.1 localhost
owner: root
group: root
mode: '0644'
- name: Ensure the default Apache port is 8080
ansible.builtin.lineinfile:
path: /etc/httpd/conf/httpd.conf
regexp: '^Listen '
insertafter: '^#Listen '
line: Listen 8080
- name: Ensure php extension matches new pattern
ansible.builtin.lineinfile:
path: /etc/httpd/conf/httpd.conf
search_string: '<FilesMatch ".php[45]?$">'
insertafter: '^\t<Location \/>\n'
line: ' <FilesMatch ".php[34]?$">'
- name: Ensure we have our own comment added to /etc/services
ansible.builtin.lineinfile:
path: /etc/services
regexp: '^# port for http'
insertbefore: '^www.*80/tcp'
line: '# port for http by default'
- name: Add a line to a file if the file does not exist, without passing regexp
ansible.builtin.lineinfile:
path: /tmp/testfile
line: 192.168.1.99 foo.lab.net foo
create: yes
# NOTE: Yaml requires escaping backslashes in double quotes but not in single quotes
- name: Ensure the JBoss memory settings are exactly as needed
ansible.builtin.lineinfile:
path: /opt/jboss-as/bin/standalone.conf
regexp: '^(.*)Xms(\d+)m(.*)$'
line: '\1Xms${xms}m\3'
backrefs: yes
# NOTE: Fully quoted because of the ': ' on the line. See the Gotchas in the YAML docs.
- name: Validate the sudoers file before saving
ansible.builtin.lineinfile:
path: /etc/sudoers
state: present
regexp: '^%ADMIN ALL='
line: '%ADMIN ALL=(ALL) NOPASSWD: ALL'
validate: /usr/sbin/visudo -cf %s
# See https://docs.python.org/3/library/re.html for further details on syntax
- name: Use backrefs with alternative group syntax to avoid conflicts with variable values
ansible.builtin.lineinfile:
path: /tmp/config
regexp: ^(host=).*
line: \g<1>{{ hostname }}
backrefs: yes
- name: sudoers添加gpadmin权限
lineinfile:
path: /etc/sudoers
line: 'gpadmin ALL=(ALL) ALL'
insertafter: "^root.*?ALL="
owner: root
group: root
mode: '0644'
PS
插入命令仅当文本内没有和line变量一样的行数据时执行
#ansible test -m lineinfile -a 'path=/etc/hosts line="192.168.1.184 QL" state=present insertafter="192.168.1.183"'
192.168.1.183 gp-master
192.168.1.184 QL
192.168.1.180 gp-datanode01
192.168.1.182 gp-datanode02
192.168.1.181 gp-datanode03
#执行 ansible test -m lineinfile -a 'path=/etc/hosts line="192.168.1.184 QL" state=present insertbefore="192.168.1.183"' 不发生更改,因为行数据 "192.168.1.184 QL" 已存在相同的
blockinfile 文本块操作
插入/更新/删除由标记行包围的文本块
https://docs.ansible.com/ansible/latest/collections/ansible/builtin/blockinfile_module.html#ansible-builtin-blockinfile-module-insert-update-remove-a-text-block-surrounded-by-marker-lines
命令 | 参数 | 说明 |
---|---|---|
path | path | 要修改的文件 |
block | string | 要插入标记行内的文本. 如果它是空字符串或缺失,块将被删除 |
state | absent present |
absent-删除(所有匹配) present-添加/替换(默认) |
marker | string | 标记块模板,{mark} 将被替换为marker_begin 和marker_end 中的值 # {mark} ANSIBLE MANAGED BLOCK -(默认) |
marker_begin | string | 这将被插入到{mark}块的开始标记处 BEGIN -(默认) |
marker_end | string | 这将被插入到{mark}块的结束标记处 END -(默认) |
insertbefore | string | 如果正则表达式同时传递给regexp和insertbefore,则只有在没有找到与regexp匹配的情况下才使用insertbefore. 不能与insertafter 一起使用 |
insertafter | string | 如果正则表达式同时传递给regexp和insertafter,则只有在没有找到与regexp匹配的情况下才使用insertafter. 不能与insertbefore 一起使用 |
mode | 0777 or u+rwx or u=rw,g=r,o=r |
目标文件或目录的权限 |
create | boolean | 与 state = current 一起使用 true-创建尚不存在的文件 false-(默认) |
owner | string | 应该拥有文件系统对象的用户名(chown) |
group | string | 应该拥有文件系统对象的组的名称(chown) |
backup | boolean | 备份 |
- name: Insert/Update HTML surrounded by custom markers after <body> line
ansible.builtin.blockinfile:
path: /var/www/html/index.html
marker: "<!-- {mark} ANSIBLE MANAGED BLOCK -->"
insertafter: "<body>"
block: |
<h1>Welcome to {{ ansible_hostname }}</h1>
<p>Last updated on {{ ansible_date_time.iso8601 }}</p>
- name: Insert/Update "Match User" configuration block in /etc/ssh/sshd_config
ansible.builtin.blockinfile:
path: /etc/ssh/sshd_config
block: |
Match User ansible-agent
PasswordAuthentication no
- name: Search with a multiline search flags regex and if found insert after (?m)=>多行匹配
blockinfile:
path: listener.ora
block: "{{ listener_line | indent(width=8, first=True) }}"
insertafter: '(?m)SID_LIST_LISTENER_DG =\n.*\(SID_LIST ='
marker: " <!-- {mark} ANSIBLE MANAGED BLOCK -->"
find 查找文件
https://docs.ansible.com/ansible/latest/collections/ansible/builtin/find_module.html#ansible-builtin-find-module-return-a-list-of-files-based-on-specific-criteria
命令 | 参数 | 说明 |
---|---|---|
paths | list[path] | 要搜索的目录的路径列表,所有路径必须是完全限定的 |
contains | string | 应该与文件内容匹配的正则表达式或模式( file_type=file 时生效) |
file_type | string | any-任意 directory-目录 file-文件(默认) link-链接 |
patterns | list[globs] | 一个或多个(shell 或 regex)模式,其类型由use_regex 选项控制 匹配过滤器 |
excludes | list[globs] | 同patterns 排除过滤器 |
use_regex | boolean | true-regexes(python) false-globs(shell)(默认) |
size | string | 搜索 >=size 的文件 为负时查找<= 单位: 字节B(b) 千字节KB(k) 兆字节MB(m) 千兆字节GB(g) 太字节T(t) |
age | string | 搜索 >=age 的文件 为负时查找<= 单位: 秒(s) 分(m) 小时(h) 日(d) 周(w) |
age_stamp | string | 比较文件的时间属性 atime-访问时间 mtime-修改时间 ctime-状态改变时间 |
recurse | boolean | true-递归子目录 false-不递归(默认) |
depth | integer | 递归深度(默认无限深度) |
hidden | boolean | true-包括隐藏文件 false-忽略(默认) |
#找出/opt/download/下 大于20mb的文件(递归子目录)
ansible test -m find -a "paths=/opt/download/ size=20m recurse=true"
#找出/opt/download/下 30天前创建的文件(递归子目录)
ansible test -m find -a "paths=/opt/download/ age=-30d recurse=true age_stamp=ctime"
- name: Recursively find /tmp files older than 2 days
ansible.builtin.find:
paths: /tmp
age: 2d
recurse: yes
- name: Recursively find /tmp files older than 4 weeks and equal or greater than 1 megabyte
ansible.builtin.find:
paths: /tmp
age: 4w
size: 1m
recurse: yes
- name: Recursively find /var/tmp files with last access time greater than 3600 seconds
ansible.builtin.find:
paths: /var/tmp
age: 3600
age_stamp: atime
recurse: yes
- name: Find /var/log files equal or greater than 10 megabytes ending with .old or .log.gz
ansible.builtin.find:
paths: /var/log
patterns: '*.old,*.log.gz'
size: 10m
# Note that YAML double quotes require escaping backslashes but yaml single quotes do not.
- name: Find /var/log files equal or greater than 10 megabytes ending with .old or .log.gz via regex
ansible.builtin.find:
paths: /var/log
patterns: "^.*?\\.(?:old|log\\.gz)$"
size: 10m
use_regex: yes
- name: Find /var/log all directories, exclude nginx and mysql
ansible.builtin.find:
paths: /var/log
recurse: no
file_type: directory
excludes: 'nginx,mysql'
# When using patterns that contain a comma, make sure they are formatted as lists to avoid splitting the pattern
- name: Use a single pattern that contains a comma formatted as a list
ansible.builtin.find:
paths: /var/log
file_type: file
use_regex: yes
patterns: ['^_[0-9]{2,4}_.*.log$']
- name: Use multiple patterns that contain a comma formatted as a YAML list
ansible.builtin.find:
paths: /var/log
file_type: file
use_regex: yes
patterns:
- '^_[0-9]{2,4}_.*.log$'
- '^[a-z]{1,5}_.*log$'
file 文件操作
https://docs.ansible.com/ansible/latest/collections/ansible/builtin/file_module.html#ansible-builtin-file-module-manage-files-and-file-properties
命令 | 参数 | 说明 |
---|---|---|
path | path | 文件路径 |
state | "absent" "directory" "file" "hard" "link" "touch" |
file-更新文件权限/状态(需文件存在) hard-硬链接 link-软链接 absent-递归删除目录 directory-创建目录 touch-创建文件 |
mode | 0777 / u+rwx or u=rw,g=r,o=r |
生成的文件系统对象应该具有的权限 |
src | path | 要链接到的文件的路径 |
owner | string | 应该拥有文件系统对象的用户名(chown) |
group | string | 应该拥有文件系统对象的组的名称(chown) |
force | boolean | 强制在两种情况下创建符号链接(文件不存在|目标存在并且是一个文件(删除替换)) false-(默认) |
#创建目录
ansible test -m file -a "path=/opt/download state=directory"
#创建文件
ansible test -m file -a "path=/opt/download/install.sh state=touch"
#更改权限
ansible test -m file -a "path=/opt/download/install.sh state=file mode=0777"
#创建链接(/结尾的会被认为是目录)
ansible test -m file -a "src=/opt/download path=/etc/download state=link"
- name: Change file ownership, group and permissions
ansible.builtin.file:
path: /etc/foo.conf
owner: foo
group: foo
mode: '0644'
- name: Give insecure permissions to an existing file
ansible.builtin.file:
path: /work
owner: root
group: root
mode: '1777'
copy 分发文件
https://docs.ansible.com/ansible/latest/collections/ansible/builtin/copy_module.html#ansible-builtin-copy-module-copy-files-to-remote-locations
命令 | 参数 | 说明 |
---|---|---|
src | path | 要复制到远程服务器的文件的本地路径 |
dest | path | 文件应复制到的远程绝对路径.如果dest是一个不存在的路径,并且dest以”/”结尾,或者src是一个目录,则创建dest |
content | string | 当用于代替src时,直接将文件的内容设置为指定的值. 只有dest为文件时才有效 |
mode | 0777 or u+rwx or u=rw,g=r,o=r |
目标文件或目录的权限 |
backup | yes or no |
备份 |
owner | string | 应该拥有文件系统对象的用户名(chown) |
group | string | 应该拥有文件系统对象的组的名称(chown) |
#分发文件
ansible test -m copy -a 'src=/etc/hostname dest=/tmp/'
#分发目录(/opt/download -> /opt/download)
ansible test -m copy -a 'src=/opt/download dest=/opt/'
ansible test -m copy -a 'src=/opt/download/ dest=/opt/download/'
#分发并修改权限
ansible test -m copy -a 'src=/etc/hostname dest=/tmp/ mode=644 owner=root group=root'
#将文本写到远程文件
ansible test -m copy -a 'content="QL" dest=/tmp/ql.txt'
/opt/download 目录+目录内容
/opt/download/ 目录的内容
- name: Copy file with owner and permissions
ansible.builtin.copy:
src: /srv/myfiles/foo.conf
dest: /etc/foo.conf
owner: foo
group: foo
mode: '0644'
- name: Copy file with owner and permission, using symbolic representation
ansible.builtin.copy:
src: /srv/myfiles/foo.conf
dest: /etc/foo.conf
owner: foo
group: foo
mode: u=rw,g=r,o=r
fetch 从远程节点获取文件
https://docs.ansible.com/ansible/latest/collections/ansible/builtin/fetch_module.html#ansible-builtin-fetch-module-fetch-files-from-remote-nodes
命令 | 参数 | 说明 |
---|---|---|
src | path | 要获取的远程系统上的文件(不能为目录) |
dest | path | 保存文件的目录(保存在 dest /ip/src ) |
flat | boolean | true-不保留原始目录结构复制 false-(默认) |
fail_on_missing | boolean | true-远程文件由于任何原因无法读取,任务将失败(默认) |
validate_checksum | boolean | true-在获取文件后,验证源和目标校验和是否匹配(默认) |
ansible all -m fetch -a 'src=/etc/hostname dest=/tmp/ansible'
- name: Store file into /tmp/fetched/host.example.com/tmp/somefile
ansible.builtin.fetch:
src: /tmp/somefile
dest: /tmp/fetched
- name: Specifying a path directly
ansible.builtin.fetch:
src: /tmp/somefile
dest: /tmp/prefix-{{ inventory_hostname }}
flat: yes
- name: Specifying a destination path
ansible.builtin.fetch:
src: /tmp/uniquefile
dest: /tmp/special/
flat: yes
- name: Storing in a path relative to the playbook
ansible.builtin.fetch:
src: /tmp/uniquefile
dest: special/prefix-{{ inventory_hostname }}
flat: yes
slurp 从远程节点获取文件的base64编码值
https://docs.ansible.com/ansible/latest/collections/ansible/builtin/slurp_module.html#ansible-builtin-slurp-module-slurps-a-file-from-remote-nodes
命令 | 参数 | 说明 |
---|---|---|
src | path | 要获取的远程系统上的文件(不能为目录) |
ansible test -m slurp -a 'src=/etc/hostname'
#192.168.1.180 | SUCCESS => {
# "changed": false,
# "content": "Z3AtZGF0YW5vZGUwMQo=",
# "encoding": "base64",
# "source": "/etc/hostname"
#}
- name: Find out what the remote machine's hostname are
ansible.builtin.slurp:
src: /etc/hostname
register: hostname
- name: Print returned information
ansible.builtin.debug:
msg: "{{ hostname['content'] | b64decode }}"
get_url 文件下载
类似wget
https://docs.ansible.com/ansible/latest/collections/ansible/builtin/get_url_module.html#ansible-builtin-get-url-module-downloads-files-from-http-https-or-ftp-to-node
命令 | 参数 | 说明 |
---|---|---|
url | string | 下载网址 |
dest | path | 下载文件保存的绝对路径 |
force | boolean | true-忽略并覆盖文件 false-不存在时下载(默认) |
mode | 0777 or u+rwx or u=rw,g=r,o=r |
目标文件或目录的权限 |
owner | string | 应该拥有文件系统对象的用户名(chown) |
group | string | 应该拥有文件系统对象的组的名称(chown) |
timeout | integer | URL请求的超时时间(秒) |
validate_certs | boolean | 验证SSL证书(默认) |
#下载 切记路径要以/结尾,或使用文件路径
ansible test -m get_url -a 'url=https://download.jetbrains.com/idea/ideaIU-2021.2.2.tar.gz dest=/opt/download/ force=true'
ansible test -m get_url -a 'url=https://download.jetbrains.com/idea/ideaIU-2021.2.2.tar.gz dest=/opt/download/ideaIU-2021.2.2.tar.gz force=true'
- name: Download file from a file path
ansible.builtin.get_url:
url: file:///tmp/afile.txt
dest: /tmp/afilecopy.txt
- name: Download foo.conf
ansible.builtin.get_url:
url: http://example.com/path/file.conf
dest: /etc/foo.conf
mode: '0440'
stat 检索文件或文件系统状态
文件信息收集
https://docs.ansible.com/ansible/latest/collections/ansible/builtin/stat_module.html#ansible-builtin-stat-module-retrieve-file-or-file-system-status
命令 | 参数 | 说明 |
---|---|---|
path | path | 文件/对象的完整路径 |
follow | boolean | 是否遵循符号链接 false-不遵循(默认) |
ansible test -m stat -a 'path=/usr/lib64/libc.so.6'
#"stat": {
# "atime": 1678795947.01495,
# "ctime": 1678363941.14613,
# "exists": true,
# "gr_name": "root",#Group name of owner
# "pw_name": "root",#User name of owner
# "lnk_source": "/usr/lib64/libc-2.17.so",
# "lnk_target": "libc-2.17.so",
# "mode": "0777",
# "mtime": 1678363941.14613,
# "nlink": 1,
# "path": "/usr/lib64/libc.so.6",
# "pw_name": "root",
# "size": 12,
# "version": null,
# "isdir": false,
# "islnk": true
#}
- name: Get stats of a file
ansible.builtin.stat:
path: /etc/foo.conf
register: st
- name: Get stats of the FS object
ansible.builtin.stat:
path: /path/to/something
register: p
- name: Print a debug message
ansible.builtin.debug:
msg: "Path exists and is a directory"
when: p.stat.isdir is defined and p.stat.isdir
返回值
https://docs.ansible.com/ansible/latest/collections/ansible/builtin/stat_module.html#return-values
tempfile 创建临时文件和目录
https://docs.ansible.com/ansible/latest/collections/ansible/builtin/tempfile_module.html#ansible-builtin-tempfile-module-creates-temporary-files-and-directories
命令 | 参数 | 说明 |
---|---|---|
path | path | 应该创建临时文件或目录的位置 如果未指定 path,则将使用默认的系统临时目录 |
prefix | string | 模块创建的文件/目录名后缀 "ansible." -(默认) |
suffix | string | 模块创建的文件/目录名后缀 "" -(默认) |
state | directory file |
是否创建文件或目录 "file" -(默认) |
- name: Create temporary build directory
ansible.builtin.tempfile:
state: directory
suffix: build
- name: Create temporary file
ansible.builtin.tempfile:
state: file
suffix: temp
register: tempfile_1
#"tempfile_1": {
# "changed": true,
# "failed": false,
# "gid": 0,
# "group": "root",
# "mode": "0600",
# "owner": "root",
# "path": "/tmp/ansible.2OHSfAtemp",
# "size": 0,
# "state": "file",
# "uid": 0
#}
- name: Use the registered var and the file module to remove the temporary file
ansible.builtin.file:
path: "{{ tempfile_1.path }}"
state: absent
when: tempfile_1.path is defined
(posix) patch 使用 GNU 补丁工具应用补丁文件
https://docs.ansible.com/ansible/latest/collections/ansible/posix/patch_module.html#ansible-posix-patch-module-apply-patch-files-using-the-gnu-patch-tool
命令 | 参数 | 说明 |
---|---|---|
src | path | 补丁文件路径 |
dest | path | 要修补的远程计算机上文件的路径 |
basedir | path | 将在其中应用补丁程序文件的基目录的路径(批量补丁目录下的文件). 如果指定了dest选项,则可以省略 |
state | absent present |
应用/还原补丁 present -(默认) |
remote_src | boolean | true-使用远程机器上的src文件 false-使用主控机src文件(默认) |
ignore_whitespace | boolean | true-忽略补丁和输入之间的空白更改 false-(默认) |
binary | boolean | true-禁用转换CRLF行结束到LF(src和dest的行尾必须匹配) false-替换POSIX上src文件中的CRLF(默认) |
strip | integer | 包含前导斜杠的最小前缀的数字 0-(默认) |
backup | boolean | 传递--backup --version-control=numbered 到patch命令 |
- name: Apply patch to one file
ansible.posix.patch:
src: /tmp/index.html.patch
dest: /var/www/index.html
- name: Apply patch to multiple files under basedir
ansible.posix.patch:
src: /tmp/customize.patch
basedir: /var/www
strip: 1
- name: Revert patch to one file
ansible.posix.patch:
src: /tmp/index.html.patch
dest: /var/www/index.html
state: absent
3. 服务管理
systemd 系统服务管理
类似于systemctl,一般应用于Centos 7/8
https://docs.ansible.com/ansible/latest/collections/ansible/builtin/systemd_service_module.html#ansible-builtin-systemd-service-module-manage-systemd-units
命令 | 参数 | 说明 |
---|---|---|
name | string | 服务名 |
enabled | true or false yes or no |
是否开机自启动 |
state | "reloaded" "restarted" "started" "stopped" |
restarted-关闭再开启 reloaded-重载配置 |
daemon_reload | true or false yes or no |
重启加载systemctl配置 systemctl daemon-reload |
#开机自启定时任务(crond)
ansible test -m systemd -a 'name=crond enabled=yes'
#重启服务
ansible test -m systemd -a 'name=crond state=restarted'
- name: Make sure a service unit is running
ansible.builtin.systemd:
state: started
name: httpd
- name: Stop service cron on debian, if running
ansible.builtin.systemd:
name: cron
state: stopped
service 系统服务管理
比systemd更细致,一般应用于Centos 5/6, 7/8也可以用
https://docs.ansible.com/ansible/latest/collections/ansible/builtin/service_module.html#ansible-builtin-service-module-manage-services
命令 | 参数 | 说明 |
---|---|---|
name | string | 服务名 |
enabled | false or true yes or no |
是否开机自启动 |
state | "reloaded" "restarted" "started" "stopped" |
restarted-关闭再开启 reloaded-重载配置 |
sleep | 重启操作间的延时 | |
pattern | string | 如果服务不响应status命令,则将查找的子字符串命名为ps命令的输出中可以找到的子字符串,作为状态结果的替身. 如果找到该字符串,则假定服务已启动 |
arguments/args | string | 命令行上提供的其他参数 |
#重启服务
ansible test -m systemd -a 'name=network state=restarted args=eth0'
- name: Start service httpd, if not started
ansible.builtin.service:
name: httpd
state: started
- name: Stop service httpd, if started
ansible.builtin.service:
name: httpd
state: stopped
4. 软件包管理
package 自动识别操作系统的通用包管理器
https://docs.ansible.com/ansible/latest/collections/ansible/builtin/package_module.html#ansible-builtin-package-module-generic-os-package-manager
命令 | 参数 | 说明 |
---|---|---|
name | string | 软件包名 |
state | string | present -安装 absent -卸载 latest -最新 |
use | string | 手动指定包管理器模块(yum、apt等) |
ansible test -m package -a 'name=python3 state=present'
ansible test -m package -a 'name=python3 state=absent'
- name: Install ntpdate
ansible.builtin.package:
name: ntpdate
state: present
# This uses a variable as this changes per distribution.
- name: Remove the apache package
ansible.builtin.package:
name: "{{ apache }}"
state: absent
- name: Install the latest version of Apache and MariaDB
ansible.builtin.package:
name:
- httpd
- mariadb-server
state: latest
yum_repository 配置yum源
https://docs.ansible.com/ansible/latest/collections/ansible/builtin/yum_repository_module.html#ansible-builtin-yum-repository-module-add-or-remove-yum-repositories
命令 | 参数 | 说明 |
---|---|---|
name | string | 源名 |
baseurl | string | 源baseurl地址 可以是多个url的列表 |
description | string | 源注释说明 |
enabled | boolean | 是否启用 |
file | string | repo的文件名 不带.repo扩展名.默认为name的值 |
sslverify | boolean | SSL证书验证 默认true |
gpgcheck | boolean | 对包执行GPG签名检查 |
#阿里 epel 镜像
ansible test -m yum_repository -a 'name="aliyun_epel" description="Extra Packages for Enterprise Linux 7 - $basearch" baseurl="http://mirrors.aliyun.com/epel/7/$basearch" enabled=true gpgcheck=false'
- name: Add repository
ansible.builtin.yum_repository:
name: epel
description: EPEL YUM repo
baseurl: https://download.fedoraproject.org/pub/epel/$releasever/$basearch/
- name: Add multiple repositories into the same file (1/2)
ansible.builtin.yum_repository:
name: epel
description: EPEL YUM repo
file: external_repos
baseurl: https://download.fedoraproject.org/pub/epel/$releasever/$basearch/
gpgcheck: no
- name: Add multiple repositories into the same file (2/2)
ansible.builtin.yum_repository:
name: rpmforge
description: RPMforge YUM repo
file: external_repos
baseurl: http://apt.sw.be/redhat/el7/en/$basearch/rpmforge
mirrorlist: http://mirrorlist.repoforge.org/el7/mirrors-rpmforge
enabled: no
yum yum包管理器
https://docs.ansible.com/ansible/latest/collections/ansible/builtin/yum_module.html#ansible-builtin-yum-module-manages-packages-with-the-yum-package-manager
命令 | 参数 | 说明 |
---|---|---|
name | list name>=1.0 |
软件包名(逗号分割) * -运行yum -y update |
state | "absent" "installed" "latest" "present" "removed" |
present/installed 只会确 保安装所需的包. latest 将更新该包. removed/absent 将删除指定的包. |
description | string | 源注释说明 |
enabled | boolean | 是否启用 |
file | string | repo的文件名 不带.repo扩展名.默认为name的值 |
sslverify | boolean | SSL证书验证 默认true |
gpgcheck | boolean | 对包执行GPG签名检查 |
#安装最新
ansible test -m yum_repository -a 'name=ansible state=latest'
#安装
ansible test -m yum_repository -a 'name=ansible state=latest'
- name: Install one specific version of Apache
ansible.builtin.yum:
name: httpd-2.2.29-1.4.amzn1
state: present
- name: Upgrade all packages
ansible.builtin.yum:
name: '*'
state: latest
dnf dnf包管理器
https://docs.ansible.com/ansible/latest/collections/ansible/builtin/dnf_module.html#ansible-builtin-dnf-module-manages-packages-with-the-dnf-package-manager
命令 | 参数 | 说明 |
---|---|---|
name | list | 软件包名(逗号分割) |
state | "absent" "installed" "latest" "present" "removed" |
present/installed 只会确 保安装所需的包. latest 将更新该包. removed/absent 将删除指定的包. |
autoremove | boolean | true-从系统中删除所有(不被需要的)“叶子”依赖包 |
allowerasing | boolean | true-允许删除已安装的包以解决依赖关系 false-默认 |
file | string | repo的文件名 不带.repo扩展名.默认为name的值 |
sslverify | boolean | SSL证书验证 默认true |
#安装最新
ansible test -m yum_repository -a 'name=ansible state=latest'
#安装
ansible test -m yum_repository -a 'name=ansible state=latest'
- name: Install Apache >= 2.4
ansible.builtin.dnf:
name: httpd >= 2.4
state: present
- name: Upgrade all packages
ansible.builtin.dnf:
name: "*"
state: latest
pip python包管理器
https://docs.ansible.com/ansible/latest/collections/ansible/builtin/pip_module.html#ansible-builtin-pip-module-manages-python-library-dependencies
命令 | 参数 | 说明 |
---|---|---|
name | list[string] | 要安装的Python库名称或远程包的url |
state | present absent forcereinstall latest |
present -安装 absent -卸载 forcereinstall -强制重新安装 latest -最新版 |
version | string | name 参数中指定的 Python 库的安装版本号 |
chdir | path | 执行命令前请进入该目录(cd path) |
extra_args | string | 额外的参数传递给pip |
requirements | string | pip需求文件的路径,它应该是远程系统的本地路径 |
executable | path | pip可执行文件的显式可执行文件或路径名 |
- name: Install bottle python package with version specifiers
ansible.builtin.pip:
name: bottle>0.10,<0.20,!=0.11
- name: Install specified python requirements and custom Index URL
ansible.builtin.pip:
requirements: /my_app/requirements.txt
extra_args: -i https://example.com/pypi/simple
- name: Install specified python requirements offline from a local directory with downloaded packages
ansible.builtin.pip:
requirements: /my_app/requirements.txt
extra_args: "--no-index --find-links=file:///my_downloaded_packages_dir"
- name: Install bottle for Python 3.3 specifically, using the 'pip3.3' executable
ansible.builtin.pip:
name: bottle
executable: pip3.3
- name: Install bottle, forcing reinstallation if it's already installed
ansible.builtin.pip:
name: bottle
state: forcereinstall
5. 系统管理
(posix) mount 挂载远程文件系统
https://docs.ansible.com/ansible/latest/collections/ansible/posix/mount_module.html?q=mount&check_keywords=yes&area=default#ansible-posix-mount-module-control-active-and-configured-mount-points
命令 | 参数 | 说明 |
---|---|---|
src | path | 要挂载的设备链接 |
path | path | 挂载点的路径 |
fstype | string | 文件系统类型(nfs ) |
state | mounted unmounted remounted present absent absent_from_fstab ephemeral |
mounted-挂载 unmounted-卸载,不修改fstab remounted-重新挂载 present-仅配置fstab,不触发或需要挂载 absent-从fstab中删除,卸载设备并删除挂载点 absent_from_fstab-从fstab中删除,不卸载或删除挂载点 ephemeral-仅挂载的设备,不更改fstab.如果已经挂载,将触发重新挂载 |
backup | boolean | 创建一个包含时间戳信息的备份文件 |
#显示目标设备可挂载区
ansible test -a 'showmount -e 192.168.1.183'
#查看fstab
ansible test -a 'grep wwwroot /etc/fstab'
#挂载 183的/www/wwwroot -> /opt/smb
ansible test -m mount -a 'src=192.168.1.183:/www/wwwroot path=/opt/smb/ fstype=nfs state=mounted'
#卸载
ansible test -m mount -a 'src=192.168.1.183:/www/wwwroot path=/opt/smb/ fstype=nfs state=unmounted'
# Before 2.3, option 'name' was used instead of 'path'
- name: Mount DVD read-only
ansible.posix.mount:
path: /mnt/dvd
src: /dev/sr0
fstype: iso9660
opts: ro,noauto
state: present
- name: Mount up device by label
ansible.posix.mount:
path: /srv/disk
src: LABEL=SOME_LABEL
fstype: ext4
state: present
- name: Mount up device by UUID
ansible.posix.mount:
path: /home
src: UUID=b3e48f45-f933-4c8e-a700-22a159ec9077
fstype: xfs
opts: noatime
state: present
- name: Unmount a mounted volume
ansible.posix.mount:
path: /tmp/mnt-pnt
state: unmounted
- name: Remount a mounted volume
ansible.posix.mount:
path: /tmp/mnt-pnt
state: remounted
# The following will not save changes to fstab, and only be temporary until
# a reboot, or until calling "state: unmounted" followed by "state: mounted"
# on the same "path"
- name: Remount a mounted volume and append exec to the existing options
ansible.posix.mount:
path: /tmp
state: remounted
opts: exec
- name: Mount and bind a volume
ansible.posix.mount:
path: /system/new_volume/boot
src: /boot
opts: bind
state: mounted
fstype: none
- name: Mount an NFS volume
ansible.posix.mount:
src: 192.168.1.100:/nfs/ssd/shared_data
path: /mnt/shared_data
opts: rw,sync,hard
state: mounted
fstype: nfs
- name: Mount NFS volumes with noauto according to boot option
ansible.posix.mount:
src: 192.168.1.100:/nfs/ssd/shared_data
path: /mnt/shared_data
opts: rw,sync,hard
boot: false
state: mounted
fstype: nfs
- name: Mount ephemeral SMB volume
ansible.posix.mount:
src: //192.168.1.200/share
path: /mnt/smb_share
opts: "rw,vers=3,file_mode=0600,dir_mode=0700,dom={{ ad_domain }},username={{ ad_username }},password={{ ad_password }}"
fstype: cifs
state: ephemeral
cron 定时任务模块
可指定 分
时
日
月
周
https://docs.ansible.com/ansible/latest/collections/ansible/builtin/cron_module.html#ansible-builtin-cron-module-manage-cron-d-and-crontab-entries
命令 | 参数 | 说明 |
---|---|---|
name | string | 定时任务名 |
minute | string * */2 |
分 |
hour | string | 时 |
day | string | 日 |
month | string | 月 |
weekday | string | 周 |
job | string | 要执行的命令 |
state | absent present |
absent-删除 present-添加(默认) |
disabled | boolean | 该作业是否应该在crontab中禁用(注释). 仅当state=present时有效 |
env | boolean | true-则设置环境变量(Name和Job分别为环境变量的名称和值) |
#查看设备的cron执行列表
ansible test -a 'crontab -l'
#手动配置列表
crontab -e
#每2分钟更新一次ntp时间
ansible test -m cron -a 'name="sync time" minute="*/2" job="/sbin/ntpdate ntp1.aliyun.com &>/dev/null" state=present'
#删除指定cron
ansible test -m cron -a 'name="sync time" state=absent'
#注释指定cron
ansible test -m cron -a 'name="sync time" state=absent disabled=true'
- name: Creates an entry like "@reboot /some/job.sh"
ansible.builtin.cron:
name: "a job for reboot"
special_time: reboot
job: "/some/job.sh"
- name: Creates an entry like "PATH=/opt/bin" on top of crontab
ansible.builtin.cron:
name: PATH
env: yes
job: /opt/bin
hostname hostname管理
https://docs.ansible.com/ansible/latest/collections/ansible/builtin/hostname_module.html#ansible-builtin-hostname-module-manage-hostname
命令 | 参数 | 说明 |
---|---|---|
name | string | 定时任务名 |
use | alpine debian freebsd generic macos macosx darwin openbsd openrc redhat sles solaris systemd |
使用哪种策略来更新主机名. 为空-自动检测 systemd-Centos7 |
ansible test -m hostname -a 'name="QL" use=systemd'
- name: Set a hostname
ansible.builtin.hostname:
name: web01
- name: Set a hostname specifying strategy
ansible.builtin.hostname:
name: web01
use: systemd
known_hosts 添加或移除known_hosts
https://docs.ansible.com/ansible/latest/collections/ansible/builtin/known_hosts_module.html#ansible-builtin-known-hosts-module-add-or-remove-a-host-from-the-known-hosts-file
命令 | 参数 | 说明 |
---|---|---|
name | string | 要添加或删除的主机(必须与key中指定的主机匹配). 它将被转换为小写,以便ssh-keygen可以找到它 |
key | <hostname[,IP]> ssh-rsa <pubkey> |
组的可选GID |
state | absent present |
absent-删除 present-添加(默认) |
path | path | 要编辑的known_hosts文件 |
- name: Tell the host about our servers it might want to ssh to
ansible.builtin.known_hosts:
path: /etc/ssh/ssh_known_hosts
name: foo.com.invalid
key: "{{ lookup('ansible.builtin.file', 'pubkeys/foo.com.invalid') }}"
- name: Another way to call known_hosts
ansible.builtin.known_hosts:
name: host1.example.com # or 10.9.8.77
key: host1.example.com,10.9.8.77 ssh-rsa ASDeararAIUHI324324 # some key gibberish
path: /etc/ssh/ssh_known_hosts
state: present
- name: Add host with custom SSH port
ansible.builtin.known_hosts:
name: '[host1.example.com]:2222'
key: '[host1.example.com]:2222 ssh-rsa ASDeararAIUHI324324' # some key gibberish
path: /etc/ssh/ssh_known_hosts
state: present
- name: Ensure server is present in known_hosts file
ansible.builtin.known_hosts:
name:"QL"
state: present
key:"{{ lookup('pipe', 'ssh-keyscan QL') }}"
hash_host: true
reboot 重启并等待
https://docs.ansible.com/ansible/latest/collections/ansible/builtin/debug_module.html#ansible-builtin-debug-module-print-statements-during-execution
命令 | 参数 | 说明 |
---|---|---|
msg | string | 在重新启动之前显示给用户的消息 |
test_command | string | 命令在重新启动的主机上运行,并期望从中获得成功,以确定计算机已准备好执行进一步的任务 |
post_reboot_delay | integer | 重新启动命令成功后,在尝试验证系统成功重新启动之前需要等待几秒钟 0-(默认) |
pre_reboot_delay | integer | 重启前需要等待几秒. 在Linux、macOS和OpenBSD上,这个时间转换为分钟并四舍五入. 如果小于60,它将被设置为0. 在Solaris和FreeBSD上,这将是几秒钟 0-(默认) |
reboot_timeout | integer | 等待机器重新启动并响应测试命令的最大秒数 600-(默认) |
connect_timeout | integer | 在再次尝试之前等待成功连接到托管主机的最大秒数 |
ansible test -m reboot
ansible test -m reboot -a 'reboot_timeout=3600 test_command=whoami'
- name: Unconditionally reboot the machine with all defaults
ansible.builtin.reboot:
- name: Reboot a slow machine that might have lots of updates to apply
ansible.builtin.reboot:
reboot_timeout: 3600
- name: Reboot a machine with shutdown command in unusual place
ansible.builtin.reboot:
search_paths:
- '/lib/molly-guard'
- name: Reboot machine using a custom reboot command
ansible.builtin.reboot:
reboot_command: launchctl reboot userspace
boot_time_command: uptime | cut -d ' ' -f 5
(posix) authorized_key 添加或删除 SSH 授权密钥
https://docs.ansible.com/ansible/latest/collections/ansible/posix/authorized_key_module.html#ansible-posix-authorized-key-module-adds-or-removes-an-ssh-authorized-key
命令 | 参数 | 说明 |
---|---|---|
key | string | SSH 公钥(字符串或URL) |
user | string | 远程主机上的用户名 |
path | ~/.ssh/authorized_keys -(默认) |
authorized_keys文件的替代路径 |
state | absent present -(默认) |
给定的键(带有给定的key_options)是否应该在文件中 |
key_options | string | authorized_keys文件中密钥前的ssh密钥选项字符串 |
follow | boolean | true-遵循路径符号链接 false-(默认) |
exclusive | boolean | true-从authorized_keys文件中删除所有其他非指定的密钥 false-(默认) |
manage_dir | boolean | 是否应该管理授权密钥文件的目录,使用自定义目录时请设置此项为false |
validate_certs | boolean | true-不验证SSL证书(默认) |
- name: Set authorized key taken from file
ansible.posix.authorized_key:
user: charlie
state: present
key: "{{ lookup('file', '/home/charlie/.ssh/id_rsa.pub') }}"
- name: Set authorized keys taken from url
ansible.posix.authorized_key:
user: charlie
state: present
key: https://github.com/charlie.keys
- name: Set authorized keys taken from url using lookup
ansible.posix.authorized_key:
user: charlie
state: present
key: "{{ lookup('url', 'https://github.com/charlie.keys', split_lines=False) }}"
- name: Set authorized key in alternate location
ansible.posix.authorized_key:
user: charlie
state: present
key: "{{ lookup('file', '/home/charlie/.ssh/id_rsa.pub') }}"
path: /etc/ssh/authorized_keys/charlie
manage_dir: false
- name: Set up multiple authorized keys
ansible.posix.authorized_key:
user: deploy
state: present
key: '{{ item }}'
with_file:
- public_keys/doe-jane
- public_keys/doe-john
- name: Set authorized key defining key options
ansible.posix.authorized_key:
user: charlie
state: present
key: "{{ lookup('file', '/home/charlie/.ssh/id_rsa.pub') }}"
key_options: 'no-port-forwarding,from="10.0.1.1"'
- name: Set authorized key without validating the TLS/SSL certificates
ansible.posix.authorized_key:
user: charlie
state: present
key: https://github.com/user.keys
validate_certs: false
- name: Set authorized key, removing all the authorized keys already set
ansible.posix.authorized_key:
user: root
key: "{{ lookup('file', 'public_keys/doe-jane') }}"
state: present
exclusive: true
- name: Set authorized key for user ubuntu copying it from current user
ansible.posix.authorized_key:
user: ubuntu
state: present
key: "{{ lookup('file', lookup('env','HOME') + '/.ssh/id_rsa.pub') }}"
(posix) firewalld 使用防火墙管理任意端口/服务
https://docs.ansible.com/ansible/latest/collections/ansible/posix/firewalld_module.html#ansible-posix-firewalld-module-manage-arbitrary-ports-services-with-firewalld
命令 | 参数 | 说明 |
---|---|---|
port | string | 要在防火墙中添加/删除的端口或端口范围的名称 |
service | string | 要在防火墙中添加/删除的服务的名称 |
source | string 192.0.2.0/24 |
要从防火墙中添加/删除的源/网络 |
masquerade | string | 要在防火墙内启用/禁用到/来自区域的伪装设置 |
interface | string eth2 |
要在防火墙中的区域中添加/删除的接口 |
icmp_block | string echo-request |
要在防火墙中的区域中添加/删除的 ICMP 块 |
icmp_block_inversion | boolean | 启用/禁用防火墙中某个区域的 ICMP 块的反转功能 |
zone | block dmz drop external home internal public trusted work |
防火墙区域 |
target | default ACCEPT DROP %%REJECT%% |
防火墙区域目标. 如果state被设置为absent,值将变为default |
state | absent present disabled enabled |
启用或禁用设置 |
permanent | boolean | 这个配置是在正在运行的防火墙配置中还是在重新启动时保持 |
immediate | boolean | 如果将此配置设置为永久配置,则应立即应用该配置 |
offline | boolean | 防火墙离线时是否运行此模块 |
rich_rule | string | 在防火墙中添加/删除的富规则 rules |
- name: permit traffic in default zone for https service
ansible.posix.firewalld:
service: https
permanent: true
state: enabled
- name: do not permit traffic in default zone on port 8081/tcp
ansible.posix.firewalld:
port: 8081/tcp
permanent: true
state: disabled
- ansible.posix.firewalld:
port: 161-162/udp
permanent: true
state: enabled
- ansible.posix.firewalld:
zone: dmz
service: http
permanent: true
state: enabled
- ansible.posix.firewalld:
rich_rule: rule service name="ftp" audit limit value="1/m" accept
permanent: true
state: enabled
- ansible.posix.firewalld:
source: 192.0.2.0/24
zone: internal
state: enabled
- ansible.posix.firewalld:
zone: trusted
interface: eth2
permanent: true
state: enabled
- ansible.posix.firewalld:
masquerade: true
state: enabled
permanent: true
zone: dmz
- ansible.posix.firewalld:
zone: custom
state: present
permanent: true
- ansible.posix.firewalld:
zone: drop
state: enabled
permanent: true
icmp_block_inversion: true
- ansible.posix.firewalld:
zone: drop
state: enabled
permanent: true
icmp_block: echo-request
- ansible.posix.firewalld:
zone: internal
state: present
permanent: true
target: ACCEPT
- name: Redirect port 443 to 8443 with Rich Rule
ansible.posix.firewalld:
rich_rule: rule family=ipv4 forward-port port=443 protocol=tcp to-port=8443
zone: public
permanent: true
immediate: true
state: enabled
(posix) seboolean 切换SELinux布尔值
https://docs.ansible.com/ansible/latest/collections/ansible/posix/seboolean_module.html#ansible-posix-seboolean-module-toggles-selinux-booleans
命令 | 参数 | 说明 |
---|---|---|
name | string | 要配置的布尔值的名 |
state | boolean | 期望的布尔值 |
persistent | boolean | true-设置在重新启动后仍然存在 false-(默认) |
ignore_selinux_state | boolean | 对于无法获得真正 SELinux 状态的场景(chroted 环境)非常有用 |
- name: Set httpd_can_network_connect flag on and keep it persistent across reboots
ansible.posix.seboolean:
name: httpd_can_network_connect
state: true
persistent: true
(posix) selinux 更改 SELinux 的策略和状态
https://docs.ansible.com/ansible/latest/collections/ansible/posix/selinux_module.html#ansible-posix-selinux-module-change-policy-and-state-of-selinux
命令 | 参数 | 说明 |
---|---|---|
state | disabled enforcing permissive |
SELinux模式 Permissve Mode(宽容模式) Enfocing mode(强制模式) disabled(禁用) |
configfile | string /etc/selinux/config |
SELinux 配置文件的路径 |
policy | string | SELinux 策略的名称 |
update_kernel_param | boolean | true-在禁用/启用 SELinux 时会更新内核引导参数 false-(默认) |
- name: Enable SELinux
ansible.posix.selinux:
policy: targeted
state: enforcing
- name: Put SELinux in permissive mode, logging actions that would be blocked.
ansible.posix.selinux:
policy: targeted
state: permissive
- name: Disable SELinux
ansible.posix.selinux:
state: disabled
(posix) sysctl 管理 sysctl.conf 中的条目
https://docs.ansible.com/ansible/latest/collections/ansible/posix/sysctl_module.html#ansible-posix-sysctl-module-manage-entries-in-sysctl-conf
命令 | 参数 | 说明 |
---|---|---|
name | string | 指定 sysctl 变量的点分隔路径,变量的Key |
value | string | sysctl键的期望值 |
state | present absent |
该条目在sysctl文件中是否存在 present -(默认) |
reload | boolean | true-如果sysctl_file被更新,则执行/sbin/sysctl -p (默认) |
sysctl_set | boolean | true-使用sysctl命令验证值,并在必要时使用-w false-(默认) |
ignoreerrors | boolean | true-忽略关于未知键的错误 false-(默认) |
sysctl_file | string /etc/sysctl.conf |
自定义sysctl.conf的绝对路径 |
# Set vm.swappiness to 5 in /etc/sysctl.conf
- ansible.posix.sysctl:
name: vm.swappiness
value: '5'
state: present
# Remove kernel.panic entry from /etc/sysctl.conf
- ansible.posix.sysctl:
name: kernel.panic
state: absent
sysctl_file: /etc/sysctl.conf
# Set kernel.panic to 3 in /tmp/test_sysctl.conf
- ansible.posix.sysctl:
name: kernel.panic
value: '3'
sysctl_file: /tmp/test_sysctl.conf
reload: false
# Set ip forwarding on in /proc and verify token value with the sysctl command
- ansible.posix.sysctl:
name: net.ipv4.ip_forward
value: '1'
sysctl_set: true
# Set ip forwarding on in /proc and in the sysctl file and reload if necessary
- ansible.posix.sysctl:
name: net.ipv4.ip_forward
value: '1'
sysctl_set: true
state: present
reload: true
6. 用户管理
group 用户组管理
https://docs.ansible.com/ansible/latest/collections/ansible/builtin/group_module.html#ansible-builtin-group-module-add-or-remove-groups
命令 | 参数 | 说明 |
---|---|---|
name | string | 组的名称 |
gid | integer | 组的可选GID |
state | absent present |
absent-删除 present-添加(默认) |
system | boolean | true-创建的组为系统组 false-默认 |
#添加gpadmin组
ansible test -m group -a 'name=gpadmin state=present gid=520'
- name: Ensure group "somegroup" exists
ansible.builtin.group:
name: somegroup
state: present
- name: Ensure group "docker" exists with correct gid
ansible.builtin.group:
name: docker
state: present
gid: 1750
user 用户管理
https://docs.ansible.com/ansible/latest/collections/ansible/builtin/user_module.html#ansible-builtin-user-module-manage-user-accounts
命令 | 参数 | 说明 |
---|---|---|
uid | integer | 用户的UID |
name | string | 要 创建/删除/修改 的用户名 |
group | string | 用户的主组 |
groups | list | 用户将被添加到的组列表(逗号分隔). 当设置为”时,用户将从除主组外的所有组中删除 |
home | path | 设置用户的主目录 |
password | string | 加密哈希(Linux)或明文密码(macOS) |
state | absent present |
absent-删除 present-添加(默认) |
force | boolean | true-强制删除受支持平台上的用户和相关目录(userdel –force),仅在state=absent时删除生效 false-默认 |
generate_ssh_key | boolean | 是否为用户生成SSH密钥, force=true时会覆盖现有的SSH密钥 |
append | boolean | 追加模式 true-添加到groups中指定的组中 false-(默认)只添加到groups中指定的组,并从所有其他组中删除 |
shell | /bin/bash | 指定sh运行程序 |
role | string | 设置用户的角色(逗号分隔). 删除所有角色 role=” |
system | boolean | true-使用户成为系统帐户,仅在state=present时生效 false-默认 |
create_home | boolean | 在帐户创建时或在主目录不存在时,将为用户创建一个主目录 true-默认 |
remove | boolean | true-删除与用户关联的目录(userdel),仅在state=absent时生效 false-默认 |
#添加gpadmin用户
ansible test -m user -a 'name=gpadmin uid=520 state=present home=/home/gpadmin group=gpadmin'
#查询用户
ansible test -a 'id gpadmin'
ansible test -m user -a 'password_hash("123456")'
- name: Add the user 'johnd' with a specific uid and a primary group of 'admin'
ansible.builtin.user:
name: johnd
comment: John Doe
uid: 1040
group: admin
- name: Add the user 'james' with a bash shell, appending the group 'admins' and 'developers' to the user's groups
ansible.builtin.user:
name: james
shell: /bin/bash
groups: admins,developers
append: yes
7. 加/解压
unarchive 解压
可处理 .gz
.bz2
.xz
.zst
.tar
.zip
https://docs.ansible.com/ansible/latest/collections/ansible/builtin/unarchive_module.html#ansible-builtin-unarchive-module-unpacks-an-archive-after-optionally-copying-it-from-the-local-machine
命令 | 参数 | 说明 |
---|---|---|
src | list[path] | 要解压缩的现有存档文件的路径,可以为网址 |
dest | path | 解压缩到的远程绝对路径,指定的路径必须存在 |
exclude | list[path] | 解压排除的目录和文件条目, 与include互斥 (逗号分割) |
include | list[path] | 待解压的目录和文件条目的列表. 如果include不为空,则只提取列出的文件. 与exclude互斥 (逗号分割) |
remote_src | boolean | true-使用目标服务器的存档文件 false-将本地存档文件复制到目标服务器的本地路径(默认) |
keep_newer | boolean | 不要替换比存档中的文件更新的现有文件 false-默认 |
list_files | boolean | 如果设置为True,则返回tarball中包含的文件列表 |
mode | 0777 or u+rwx or u=rw,g=r,o=r |
目标归档文件的权限 |
owner | string | 应该拥有文件系统对象的用户名(chown) |
group | string | 应该拥有文件系统对象的组的名称(chown) |
validate_certs | boolean | 证书验证 |
#项目文件压缩,排除 /target 和 /src/test
ansible test -m unarchive -a 'src=/opt/pt.zip dest=/opt/download/pt2'
- name: Extract foo.tgz into /var/lib/foo
ansible.builtin.unarchive:
src: foo.tgz
dest: /var/lib/foo
- name: Unarchive a file that is already on the remote machine
ansible.builtin.unarchive:
src: /tmp/foo.zip
dest: /usr/local/bin
remote_src: yes
archive 压缩
支持格式 bz2
gz
tar
xz
zip
https://docs.ansible.com/ansible/latest/collections/community/general/archive_module.html#community-general-archive-module-creates-a-compressed-archive-of-one-or-more-files-or-trees
命令 | 参数 | 说明 |
---|---|---|
path | list[path] | 要压缩或存档的文件的远程绝对路径,通配符或路径或通配符列表 (/opt/*)(逗号分割) |
dest | path | 目标归档文件的文件名(父目录必须在远端主机上存在). 如果目标存档已经存在,它将被截断和覆盖 |
format | "bz2" "gz" "tar" "xz" "zip" |
要使用的压缩类型 gz-默认 |
remove | boolean | 添加到归档后删除所有添加的源文件和树 |
exclude_path | list[path] | 要排除的文件的远程绝对路径,通配符或路径或通配符列表 (/opt/soft/*)(逗号分割) |
exclusion_patterns | list[path] | 使用通配符模式将文件或目录从结果存档中排除(逗号分割) |
force_archive | boolean | 允许您强制模块将其视为存档,即使只指定了一个文件 |
mode | 0777 or u+rwx or u=rw,g=r,o=r |
目标归档文件的权限 |
owner | string | 应该拥有文件系统对象的用户名(chown) |
group | string | 应该拥有文件系统对象的组的名称(chown) |
#压缩/data/greenplum/
ansible gp-master -m archive -a 'path=/data/greenplum/ dest=/data/greenplum/gpdb6.10.1.tar group=gpadmin owner=gpadmin'
#单文件压缩
ansible test -m archive -a 'path=/tmp/ql.txt dest=/tmp/ql.zip format=zip'
#项目文件压缩,排除 /target 和 /src/test (path=/opt/pt 会包含 /pt 文件夹 path=/opt/pt/ 只会压缩里面的文件)
ansible test -m archive -a 'path=/opt/pt dest=/opt/pt.zip format=zip exclusion_patterns="/opt/pt/target/*,/opt/pt/src/test/*,/opt/pt/.git/*,/opt/pt/.idea/*"'
- name: 创建bz2压缩包,并屏蔽指定文件
community.general.archive:
path:
- /path/to/foo/*
dest: /path/file.tar.bz2
exclude_path:
- /path/to/foo/ba*
exclusion_patterns:
- /path/to/foo/ball/*
format: bz2
8. 数据库
https://docs.ansible.com/ansible/latest/collections/index_module.html#community-mysql
名称 | 说明 |
---|---|
mysql_db | 从远程主机添加或删除MySQL数据库 |
mysql_info | 收集有关MySQL服务器的信息 |
mysql_query | 运行MySQL查询 |
mysql_replication | 管理MySQL复制 |
mysql_role | 添加、删除或更新MySQL角色 |
mysql_user | 从MySQL数据库中添加或删除一个用户 |
mysql_variables | 管理MySQL全局变量 |
9. 版本管理
subversion svn项目管理
https://docs.ansible.com/ansible/latest/collections/ansible/builtin/subversion_module.html#ansible-builtin-subversion-module-deploys-a-subversion-repository
命令 | 参数 | 说明 |
---|---|---|
repo | string | 存储库URL |
dest | path | 保存目录 |
username | string | 用户名 |
password | string | 密码 |
force | boolean | true-修改后的文件将被丢弃 false-如果遇到修改过的文件,模块将失败(默认) |
checkout | boolean | true-签出存储库(默认) |
export | boolean | true-导出 |
#签出ptWeb
ansible test -m subversion -a 'repo=https://192.168.1.11/svn/ptWeb dest=/opt/download/ptWeb username=QL password=xxxxxx'
- name: Checkout subversion repository to specified folder
ansible.builtin.subversion:
repo: svn+ssh://an.example.org/path/to/repo
dest: /src/checkout
- name: Export subversion directory to folder
ansible.builtin.subversion:
repo: svn+ssh://an.example.org/path/to/repo
dest: /src/export
export: yes
- name: Get information about the repository whether or not it has already been cloned locally
ansible.builtin.subversion:
repo: svn+ssh://an.example.org/path/to/repo
dest: /src/checkout
checkout: no
update: no
git git项目管理
https://docs.ansible.com/ansible/latest/collections/ansible/builtin/git_module.html#ansible-builtin-git-module-deploy-software-or-files-from-git-checkouts
命令 | 参数 | 说明 |
---|---|---|
repo | string | git,SSH或HTTP(S)协议地址 |
dest | path | 签出存储库的路径(目标目录必须为空) |
clone | boolean | false-不克隆 true-(默认) |
force | boolean | true-工作存储库中的任何修改文件都将被丢弃 false-(默认) |
update | boolean | false-不从原始存储库检索新的修订 true-(默认) |
version | string | 签出存储库的哪个版本(HEAD ,SHA-1 hash ,name ) |
depth | integer | 创建一个浅克隆,其历史记录被截断为指定的数字或版本. 最小值为1 |
remote | string | 远端的名称 origin -(默认) |
recursive | integer | false-克隆存储库时不带-recursive 选项 true-(默认) |
refspec | string | 与git fetch 命令相同的语法 |
track_submodules | boolean | 相当于为git子模块update指定-remote 标志 false-(默认) |
single_branch | boolean | true-只克隆指向指定修订的尖端的历史记录 false-(默认) |
reference | string | 参考存储库(参见git clone -reference… ) |
#签出ptWeb
ansible test -m git -a 'repo=https://192.168.1.11/svn/ptWeb dest=/opt/download/ptWeb'
- name: Git checkout
ansible.builtin.git:
repo: 'https://foosball.example.org/path/to/repo.git'
dest: /srv/checkout
version: release-0.22
- name: Read-write git checkout from github
ansible.builtin.git:
repo: git@github.com:mylogin/hello.git
dest: /home/mylogin/hello
- name: Just ensuring the repo checkout exists
ansible.builtin.git:
repo: 'https://foosball.example.org/path/to/repo.git'
dest: /srv/checkout
update: no
- name: Just get information about the repository whether or not it has already been cloned locally
ansible.builtin.git:
repo: 'https://foosball.example.org/path/to/repo.git'
dest: /srv/checkout
clone: no
update: no
- name: Checkout a github repo and use refspec to fetch all pull requests
ansible.builtin.git:
repo: https://github.com/ansible/ansible-examples.git
dest: /src/ansible-examples
refspec: '+refs/pull/*:refs/heads/*'
- name: Create git archive from repo
ansible.builtin.git:
repo: https://github.com/ansible/ansible-examples.git
dest: /src/ansible-examples
archive: /tmp/ansible-examples.zip
- name: Clone a repo with separate git directory
ansible.builtin.git:
repo: https://github.com/ansible/ansible-examples.git
dest: /src/ansible-examples
separate_git_dir: /src/ansible-examples.git
- name: Example clone of a single branch
ansible.builtin.git:
repo: https://github.com/ansible/ansible-examples.git
dest: /src/ansible-examples
single_branch: yes
version: master
- name: Avoid hanging when http(s) password is missing
ansible.builtin.git:
repo: https://github.com/ansible/could-be-a-private-repo
dest: /src/from-private-repo
environment:
GIT_TERMINAL_PROMPT: 0 # reports "terminal prompts disabled" on missing password
# or GIT_ASKPASS: /bin/true # for git before version 2.3.0, reports "Authentication failed" on missing password
10. 其他
pause 暂停
https://docs.ansible.com/ansible/latest/collections/ansible/builtin/pause_module.html#ansible-builtin-pause-module-pause-playbook-execution
命令 | 参数 | 说明 |
---|---|---|
echo | boolean | 控制在输入时是否显示键盘输入. 如果设置了’ seconds ‘或’ minutes ‘则无效 true-显示(默认) false-不显示 |
prompt | string | 用于提示消息的可选文本 |
minutes | string | 需要暂停的正数分钟 |
seconds | string | 需要暂停的正数秒数 |
#暂停5s,显示提示
ansible test -m pause -a 'seconds=5 prompt=等我5s归来'
#暂停,等待输入,不回显
ansible test -m pause -a 'echo=false prompt=输点啥'
- name: Pause for 5 minutes to build app cache
ansible.builtin.pause:
minutes: 5
- name: Pause until you can verify updates to an application were successful
ansible.builtin.pause:
- name: A helpful reminder of what to look out for post-update
ansible.builtin.pause:
prompt: "Make sure org.foo.FooOverload exception is not present"
- name: 不回显等待输入
pause:
prompt: "Enter a secret"
echo: no
register: res
- name: 输出输入值
debug:
msg: "{{ res.user_input }}"
ping 尝试连接到主机
https://docs.ansible.com/ansible/latest/collections/ansible/builtin/ping_module.html#ansible-builtin-ping-module-try-to-connect-to-host-verify-a-usable-python-and-return-pong-on-success
命令 | 参数 | 说明 |
---|---|---|
data | string | 返回ping返回值的数据 "crash" -模块将抛出异常 |
ansible all -m ping
#抛出异常
ansible all -m ping -a "data=crash"
#返回pong
ansible all -m ping -a "data=pong"
- name: Example from an Ansible Playbook
ansible.builtin.ping:
- name: Induce an exception to see what happens
ansible.builtin.ping:
data: crash
debug 调试输出
https://docs.ansible.com/ansible/latest/collections/ansible/builtin/debug_module.html#ansible-builtin-debug-module-print-statements-during-execution
命令 | 参数 | 说明 |
---|---|---|
msg | string | 输出信息 可以包含 {{变量}} |
var | string | 要调试的变量名, 与msg选项互斥. 这个选项已经在Jinja2上下文中运行,并且有一个隐式的{{}}包装 |
verbosity | integer | 0-默认 3-(-vvv) |
ansible test -m debug -a 'var=ansible_default_ipv4.address'
ansible test -m debug -a 'var=hostvars[inventory_hostname]'
- name: Print the gateway for each host when defined
ansible.builtin.debug:
msg: System {{ inventory_hostname }} has gateway {{ ansible_default_ipv4.gateway }}
when: ansible_default_ipv4.gateway is defined
- name: Print return information from the previous task
ansible.builtin.debug:
var: result
verbosity: 2
fail 自定义消息失败抛出
https://docs.ansible.com/ansible/latest/collections/ansible/builtin/fail_module.html#ansible-builtin-fail-module-fail-with-custom-message
命令 | 参数 | 说明 |
---|---|---|
msg | string | 用于执行失败的自定义消息 |
- name: Fail if the file does not belong to 'root'
ansible.builtin.fail:
msg: "Whoops! file ownership has changed"
when: st.stat.pw_name != 'root'
setup 收集指定的facts
https://docs.ansible.com/ansible/latest/collections/ansible/builtin/setup_module.html#ansible-builtin-setup-module-gathers-facts-about-remote-hosts
命令 | 参数 | 说明 |
---|---|---|
fact_path | path | 本地*.fact文件路径 |
filter | list[string] | 返回与 shell 样式(fnmatch)模式之一匹配的数据 |
gather_subset | 将收集到的信息附加在给定子集. all, all_ipv4_addresses, all_ipv6_addresses, apparmor, architecture, caps, chroot,cmdline, date_time, default_ipv4, default_ipv6, devices, distribution, distribution_major_version, distribution_release, distribution_version, dns, effective_group_ids, effective_user_id, env, facter, fips, hardware, interfaces, is_chroot, iscsi, kernel, local, lsb, machine, machine_id, mounts, network, ohai, os_family, pkg_mgr, platform, processor, processor_cores, processor_count, python, python_version, real_user_id, selinux, service_mgr, ssh_host_key_dsa_public, ssh_host_key_ecdsa_public, ssh_host_key_ed25519_public, ssh_host_key_rsa_public, ssh_host_pub_keys, ssh_pub_keys, system, system_capabilities, system_capabilities_enforced, user, user_dir, user_gecos, user_gid, user_id, user_shell, user_uid, virtual, virtualization_role, virtualization_type |
|
gather_timeout | integer | 为单独的事实收集设置默认超时(以秒为单位) |
ansible test -m setup -a 'gather_subset=network,virtual'
ansible test -m setup -a 'filter=ansible_default_ipv4'
- name: Collect only facts returned by facter
ansible.builtin.setup:
gather_subset:
- 'network'
- 'virtual'
- name: Filter and return only selected facts
ansible.builtin.setup:
filter:
- 'ansible_distribution'
- 'ansible_machine_id'
- 'ansible_*_mb'
uri 网络访问
https://docs.ansible.com/ansible/latest/collections/ansible/builtin/uri_module.html#ansible-builtin-uri-module-interacts-with-webservices
命令 | 参数 | 说明 |
---|---|---|
url | string | HTTP或HTTPS |
method | string | 请求或响应的HTTP方法 GET -(默认) |
body_format | json form-multipart form-urlencoded raw |
主体的序列化格式 raw -(默认) |
body | path | 文件/对象的完整路径 |
src | path | 要提交到远程服务器的文件的路径. 不能与body连用. 应该与force_basic_auth一起使用,以确保远程端发送401时成功 |
remote_src | boolean | false-在控制节点上搜索src true-在远程节点上搜索src |
headers | dictionary | 以YAML散列的格式向请求添加自定义HTTP标头. 在这里提供Content-Type将覆盖通过为body_format提供json或form-urlencoded生成的头文件 |
dest | path | 下载文件的路径 |
http_agent | string | 标识头,通常出现在 Web 服务器日志中 ansible-httpget -(默认) |
timeout | integer | 超时时间 30-(默认) |
status_code | list | 表示请求成功的有效的数字 HTTP 状态代码列表 [200] -(默认) |
decompress | boolean | 是否尝试解压缩 gzip 内容编码的响应 true-(默认) |
follow_redirects | safe (false) all (true) none urllib2 |
URI模块是否应该遵循重定向 safe -(默认) |
force | boolean | true-不获取缓存副本 false-(默认) |
creates | path | 如果文件名已经存在,则不会运行此步骤 |
removes | path | 如果文件名不存在,则不会运行此步骤 |
return_content | boolean | 不管成功还是失败,是否将响应体作为content 键返回到字典结果中. 独立于这个选项,如果报告的Content-type是application/json ,那么json总是被加载到字典结果中名为json 的键中 false-(默认) |
mode | 0777 or u+rwx or u=rw,g=r,o=r |
目标归档文件的权限 |
owner | string | 应该拥有文件系统对象的用户名(chown) |
group | string | 应该拥有文件系统对象的组的名称(chown) |
force_basic_auth | boolean | 在初始请求时强制发送基本身份验证标头 false-(默认) |
url_password | string | Digest, Basic or WSSE 身份验证用户密码 |
url_username | string | Digest, Basic or WSSE 身份验证用户名 |
use_proxy | boolean | 开启代理 true-(默认) |
validate_certs | boolean | 验证证书 true-(默认) |
ansible test -m uri -a 'url=https://www.baidu.com return_content=true'
- name: Check that you can connect (GET) to a page and it returns a status 200
ansible.builtin.uri:
url: http://www.example.com
- name: Check that a page returns a status 200 and fail if the word AWESOME is not in the page contents
ansible.builtin.uri:
url: http://www.example.com
return_content: true
register: this
failed_when: "'AWESOME' not in this.content"
- name: Create a JIRA issue
ansible.builtin.uri:
url: https://your.jira.example.com/rest/api/2/issue/
user: your_username
password: your_pass
method: POST
body: "{{ lookup('ansible.builtin.file','issue.json') }}"
force_basic_auth: true
status_code: 201
body_format: json
- name: Login to a form based webpage, then use the returned cookie to access the app in later tasks
ansible.builtin.uri:
url: https://your.form.based.auth.example.com/index.php
method: POST
body_format: form-urlencoded
body:
name: your_username
password: your_password
enter: Sign in
status_code: 302
register: login
- name: Login to a form based webpage using a list of tuples
ansible.builtin.uri:
url: https://your.form.based.auth.example.com/index.php
method: POST
body_format: form-urlencoded
body:
- [ name, your_username ]
- [ password, your_password ]
- [ enter, Sign in ]
status_code: 302
register: login
- name: Upload a file via multipart/form-multipart
ansible.builtin.uri:
url: https://httpbin.org/post
method: POST
body_format: form-multipart
body:
file1:
filename: /bin/true
mime_type: application/octet-stream
file2:
content: text based file content
filename: fake.txt
mime_type: text/plain
text_form_field: value
- name: Connect to website using a previously stored cookie
ansible.builtin.uri:
url: https://your.form.based.auth.example.com/dashboard.php
method: GET
return_content: true
headers:
Cookie: "{{ login.cookies_string }}"
- name: Queue build of a project in Jenkins
ansible.builtin.uri:
url: http://{{ jenkins.host }}/job/{{ jenkins.job }}/build?token={{ jenkins.token }}
user: "{{ jenkins.user }}"
password: "{{ jenkins.password }}"
method: GET
force_basic_auth: true
status_code: 201
- name: POST from contents of local file
ansible.builtin.uri:
url: https://httpbin.org/post
method: POST
src: file.json
- name: POST from contents of remote file
ansible.builtin.uri:
url: https://httpbin.org/post
method: POST
src: /path/to/my/file.json
remote_src: true
- name: Create workspaces in Log analytics Azure
ansible.builtin.uri:
url: https://www.mms.microsoft.com/Embedded/Api/ConfigDataSources/LogManagementData/Save
method: POST
body_format: json
status_code: [200, 202]
return_content: true
headers:
Content-Type: application/json
x-ms-client-workspace-path: /subscriptions/{{ sub_id }}/resourcegroups/{{ res_group }}/providers/microsoft.operationalinsights/workspaces/{{ w_spaces }}
x-ms-client-platform: ibiza
x-ms-client-auth-token: "{{ token_az }}"
body:
- name: Pause play until a URL is reachable from this host
ansible.builtin.uri:
url: "http://192.0.2.1/some/test"
follow_redirects: none
method: GET
register: _result
until: _result.status == 200
retries: 720 # 720 * 5 seconds = 1hour (60*60/5)
delay: 5 # Every 5 seconds
- name: Provide SSL/TLS ciphers as a list
uri:
url: https://example.org
ciphers:
- '@SECLEVEL=2'
- ECDH+AESGCM
- ECDH+CHACHA20
- ECDH+AES
- DHE+AES
- '!aNULL'
- '!eNULL'
- '!aDSS'
- '!SHA1'
- '!AESCCM'
- name: Provide SSL/TLS ciphers as an OpenSSL formatted cipher list
uri:
url: https://example.org
ciphers: '@SECLEVEL=2:ECDH+AESGCM:ECDH+CHACHA20:ECDH+AES:DHE+AES:!aNULL:!eNULL:!aDSS:!SHA1:!AESCCM'
wait_for 等待条件成立后继续运行
https://docs.ansible.com/ansible/latest/collections/ansible/builtin/wait_for_module.html#ansible-builtin-wait-for-module-waits-for-a-condition-before-continuing
命令 | 参数 | 说明 |
---|---|---|
connect_timeout | list[string] | 活动连接计数的TCP连接状态列表 ["ESTABLISHED", "FIN_WAIT1", "FIN_WAIT2", "SYN_RECV", "SYN_SENT", "TIME_WAIT"] -(默认) |
delay | integer | 开始轮询前等待的秒数 |
host | string | 等待的可解析主机名或IP地址 127.0.0.1 -(默认) |
port | integer | 轮询的端口号 |
path | path | 文件系统中继续之前必须存在的文件的路径。 “path”和”port”为互斥参数。 |
msg | string | 这将覆盖由于未能满足所需条件而产生的正常错误消息 |
search_regex | string | 可用于匹配文件或套接字连接中的字符串. 默认为多行正则表达式 |
sleep | integer | 检查之间的睡眠秒数 1 -(默认) |
state | string | started -检查端口开放(默认) stopped -检查端口关闭 drained -检查活动连接 absent -检查文件是否存在或已删除 present -检查文件或搜索字符串 |
timeout | integer | 等待的最大秒数 300 -(默认) |
ansible test -m wait_for -a 'host=127.0.0.1 port=6379 msg="redis未启动" state=started timeout=3'
- name: Sleep for 300 seconds and continue with play
ansible.builtin.wait_for:
timeout: 300
delegate_to: localhost
- name: Wait for port 8000 to become open on the host, don't start checking for 10 seconds
ansible.builtin.wait_for:
port: 8000
delay: 10
- name: Waits for port 8000 of any IP to close active connections, don't start checking for 10 seconds
ansible.builtin.wait_for:
host: 0.0.0.0
port: 8000
delay: 10
state: drained
meta 执行Ansible的”actions”
https://docs.ansible.com/ansible/latest/collections/ansible/builtin/meta_module.html#ansible-builtin-meta-module-execute-ansible-actions
命令 | 参数 | 说明 |
---|---|---|
free_form | string | clear_facts -清除剧中主机列表中指定的主机所收集的事实 clear_host_errors -剧本主机列表中指定的主机中清除失败状态 end_host -是 end _ play 的每个主机变体,使当前主机的剧本结束而不会失败 end_play -使剧本在没有失败的情况下结束(影响所有主机) flush_handlers -使Ansible运行已notified的handler noop -什么都不做 refresh_inventory -重新加载inventory reset_connection -中断一个持久连接 end_batch -导致当前批处理结束,而不导致主机故障 |
# Example showing flushing handlers on demand, not at end of play
- ansible.builtin.template:
src: new.j2
dest: /etc/config.txt
notify: myhandler
- name: Force all notified handlers to run at this point, not waiting for normal sync points
ansible.builtin.meta: flush_handlers
# Example showing how to end the play for specific targets
- name: End the play for hosts that run CentOS 6
ansible.builtin.meta: end_host
when:
- ansible_distribution == 'CentOS'
- ansible_distribution_major_version == '6'
assert 断言表达式
当表达式为false
时,task会抛出异常结束
https://docs.ansible.com/ansible/latest/collections/ansible/builtin/assert_module.html#ansible-builtin-assert-module-asserts-given-expressions-are-true
命令 | 参数 | 说明 |
---|---|---|
that | list[string] | 可以传递给when 语句的相同形式的字符串表达式列表 |
fail_msg | string | 用于失败断言的自定义消息 |
success_msg | string | 用于成功断言的自定义消息 |
quiet | boolean | 将此设置为true以避免冗长的输出 false-(默认) |
- name: assert test
assert:
fail_msg: "RedHat设备"
success_msg: "非RedHat设备"
that: "ansible_os_family != 'RedHat'"
quiet: true
- ansible.builtin.assert:
that:
- "'foo' in some_command_result.stdout"
- number_of_the_counting == 3
- name: After version 2.7 both 'msg' and 'fail_msg' can customize failing assertion message
ansible.builtin.assert:
that:
- my_param <= 100
- my_param >= 0
fail_msg: "'my_param' must be between 0 and 100"
success_msg: "'my_param' is between 0 and 100"
- name: Please use 'msg' when ansible version is smaller than 2.7
ansible.builtin.assert:
that:
- my_param <= 100
- my_param >= 0
msg: "'my_param' must be between 0 and 100"
- name: Use quiet to avoid verbose output
ansible.builtin.assert:
that:
- my_param <= 100
- my_param >= 0
quiet: true
4. 内置插件
Filter 过滤器
https://docs.ansible.com/ansible/latest/collections/ansible/builtin/index.html#filter-plugins
加解密
名称 | 说明 | 示例 |
---|---|---|
b64decode b64encode | Base64加解密 | {{ mypath | basename }} => ‘foo.txt’ out of ‘/etc/asdf/foo.txt’ |
checksum | SHA-1 | {{ ‘123’ | checksum }} => ’40bd001563085fc35165329ea1ff5c5ecbdbbeef’ |
sha1 | SHA-1 | {{ ‘123’ | sha1 }} => ’40bd001563085fc35165329ea1ff5c5ecbdbbeef’ |
hash | hash加密 sha1 sha256 sha3_512 shake_256 md5 blake2b sha512 sha3_224 sha3_256 shake_128 sha384 blake2s sha224 sha3_384 |
{{ ‘123’ | hash(‘md5’) }} => ‘202cb962ac59075b964b07152d234b70’ |
md5 | MD5 | {{ ‘123’ | md5 }} => ‘202cb962ac59075b964b07152d234b70’ |
password_hash | 转换输入密码为password_hash | {{ ‘123456’ | password_hash }} |
vault | vault加密 salt -加密盐(默认随机) |
{{ ‘明文’ | vault(‘密钥’) }} |
unvault | vault解密 | {{ ‘密文’ | unvault(‘密钥’) }} |
文件路径
链接 | 说明 | 示例 |
---|---|---|
basename | 获取路径的文件名(带后缀) | {{ ‘/etc/asdf/foo.txt’ | basename }} => ‘foo.txt’ |
dirname | 获取路径的目录名 | {{ ‘/etc/asdf/foo.txt’ | dirname }} => ‘/etc/asdf’ |
fileglob | 将路径glob分解为匹配文件 | {{ ‘/etc/h?sts’ | fileglob }} => [‘/etc/hosts’, ‘/etc/hasts’] |
path_join | 连接一个或多个路径 | {{ [‘/etc’, ‘apt’, ‘trusted.d’, ‘mykey.gpg’] | path_join }} => ‘/etc/apt/trusted.d/mykey.gpgp’ |
realpath | 将路径转换为实际路径(如软链接的真实路径) | {{ ‘/usr/bin/vimdiff’ | realpath }} => ‘/usr/bin/vim’ |
relpath | 获得相对路径 | {{ ‘/tmp/test/me.txt’ | relpath(‘/tmp/other/’) }} => ‘../test/me.txt’ |
splitext | 将路径拆分为根目录和文件扩展名 | {{ ‘/etc/make.conf’ | splitext }} => [ ‘/etc/make’, ‘conf’ ] |
win_basename | 获取Windows路径的基名称 | {{ ‘C:\Users\asdf\foo.txt’ | win_basename }} => ‘foo.txt’ |
win_dirname | 获取Windows路径的目录 | {{ ‘C:\Users\asdf\foo.txt’ | win_dirname }} => ‘C:\\Users\\asdf’ |
win_splitdrive | 按驱动器号拆分 Windows 路径 | {{ ‘C:\Users\asdf\foo.txt’ | win_splitdrive }} => “(‘C:’, ‘\\\\Users\\\\asdf\\\\foo.txt’)” |
数学运算
链接 | 说明 | 示例 |
---|---|---|
log | 输入数字以N为底的对数的数学运算(默认为自然对数e) | {{ 8 | log(5) }} => 1.2920296742201791 |
pow | 幂 | {{ 2 | pow(3) }} => 8 |
product | 笛卡儿积列表 | {{ [1,2] | product(repeat=2) }} => [[1,1],[1,2],[2,1],[2,2]] |
root | 开根号 | {{ 4 | root }} => 2 |
正则
链接 | 说明 | 示例 |
---|---|---|
regex_escape | 转义正则表达式字符 posix_basic python -(默认) |
{{ ‘^f.o(.)$’ | regex_escape(‘python’) }} => ‘\^f.*o(.*)\$’ |
regex_findall | 从字符串中提取所有匹配的正则表达式 ignorecase -忽略大小写 multiline -多行匹配 |
{{ ‘CAR\ntar\nfoo\nbar\n’ | regex_findall(‘^.ar$’, multiline=True, ignorecase=True) }} |
regex_replace | 通过正则表达式替换字符串 ignorecase -忽略大小写 multiline -多行匹配 |
{{ ‘ansible’ | regex_replace(‘^a.i(.)$’, ‘a\\1’) }} |
regex_search | 从字符串中提取正则表达式匹配 ignorecase -忽略大小写 multiline -多行匹配 |
{{ ‘server1/database42’ | regex_search(‘database[0-9]+’) }} |
变量处理
链接 | 说明 | 示例 |
---|---|---|
bool | 转换为布尔值 | {{ “0” | bool }} => false {{ 1 | bool }} => true |
split | 把一个字符串拆分成一个列表 " " -(默认) |
{{ ‘jojo is, a’ | split(‘,’ }} => [ “jojo is”, “a” ] |
extract | 根据索引或键提取值 | {{ 1 | extract([‘a’, ‘b’, ‘c’]) }} => [‘a’, ‘b’, ‘c’][1] => ‘b’ {{ 0 | extract([{‘a’: 1, ‘b’: 2, ‘c’: 3}, {‘x’: 9, ‘y’: 10}], morekeys=’b’) }} => ‘2’ |
comment | 注释掉字符串 | {{ ‘Plain style (default)’ | comment }} => # Plain style (default) |
human_readable | 字节大小转可读文本串 Y Z E P T G M K B |
{{ 1232345345 | human_readable }} => ‘1.15 GB’ {{ 1232345345 | human_readable(unit=’M’) }} => ‘1175.26 MB’ |
human_to_bytes | 字符串中获取字节大小 Y Z E P T G M K B |
{{ “1.15 GB” | human_to_bytes }} => 1234803098 {{ “1.15” | human_to_bytes(deafult_unit=”G”) }} => 1234803098 |
strftime | 日期格式化(10位时间戳) | {{ ‘%Y-%m-%d %H:%M:%S’ | strftime(1680278400) }} |
quote | 适当转义,防止命令语句错误 | - shell: echo {{ “hello’world” | quote }} => “echo ‘hello’\”‘\”‘world'” |
random | 随机数或列表项目(py range [a,b) ) | {{ [‘a’,’b’,’c’] | random }} {{10 | random(start=1,step=1) }} => [1,10)随机 |
mandatory | 确保变量存在或已定义 | {{ notdefined | mandatory }} => 不存在会抛”Mandatory variable ‘notdefined’ not defined.” |
ternary | 三元运算过滤器 | {{ (name ‘group1’) | ternary(‘service’, ‘systemd’) }} |
to_datetime | 从字符串获取datetime |
{{ ((‘2023-04-01 00:00:00’ |to_datetime) – (‘2023-04-02’ |to_datetime(‘%Y-%m-%d’))).total_seconds() }} => “-86400.0” |
type_debug | 显示输入数据类型 | {{ 123 | type_debug }} => ‘int’ |
文本转换
链接 | 说明 | 示例 |
---|---|---|
to_json | 将变量转换为 JSON 字符串 ensure_ascii -转义非ASCII字符(默认true) indent -缩进空格数(默认0) sort_keys -排序键名(默认false) |
{{ temp | to_json(ensure_ascii=false,sort_keys=true) }} |
to_nice_json | 将变量转换为“精心格式化的”JSON 字符串 参数同上 | {{ temp | to_nice_json }} |
to_nice_yaml | sort_keys -排序键名(默认false) |
{{ temp | to_nice_yaml }} |
to_uuid | 对指定数据转化为uuid(非随机,同数据的结果每次都一致) | {{ 123 | to_uuid }} => ‘f9a0d8fa-35e1-516f-b1bf-bb8f6c322ec4’ |
to_yaml | 将变量转换为YAML字符串 sort_keys -排序键名(默认false) |
{{ github_workflow | to_yaml }} |
from_json | 将JSON字符串转换为变量结构 | {{ ‘{“a”: true, “b”: 54, “c”: [1,2,3]}’ | from_json }} |
from_yaml | 将YAML字符串转换为变量结构 | |
from_yaml_all | 将一些YAML文档转换为可变结构 | |
urlsplit | 获取网址里的信息 fragment hostname path port query scheme netloc password username <scheme>://<netloc>/<path>;<params>?<query>#<fragment> netloc => <username>:<password>@<hostname>:<port> |
{{ “http://user:password@www.acme.com:9000/dir/index.html?query=term#fragment” | urlsplit(“hostname”) }} => ‘www.acme.com’ |
数组/字典操作
链接 | 说明 | 示例 |
---|---|---|
intersect | 列表的交集 | {{ [1, 2, 3, 6] | intersect([1, 2, 3, 4, 5]) }} => [1, 2, 3] |
subelements | 返回列表及其元素的乘积 | |
union | 列表的并集 | {{ [1, 2, 5, 1, 3, 4, 10] | union([3, 4, 5, 11, 99]) }} => [1, 2, 5, 1, 3, 4, 10, 11, 99] |
unique | 列表中的一组唯一项 | {{ [1, 2, 5, 1, 3, 4, 10] | unique }} => [1, 2, 5, 3, 4, 10] |
symmetric_difference | 两个列表的不同项目 | {{ [1, 2, 5, 1, 3, 4, 10] | symmetric_difference([1, 2, 3, 4, 5, 11, 99]) }} => [10, 11, 99] |
shuffle | 随机打乱列表 | {{ [‘a’,’b’,’c’] | shuffle}} |
rekey_on_member | 使用成员将字词列表重新键入到字词列表中 | {{ [{“ip”: “127.0.0.1”, “name”: “G1”}, {“ip”: “127.0.0.2”, “name”: “G2”}] | rekey_on_member(“ip”) }} => {“127.0.0.1”:{“ip”:”127.0.0.1″,”name”:”G1″},”127.0.0.2″:{“ip”:”127.0.0.2″,”name”:”G2″}} |
permutations | 列表元素的排列A | {{ [1,2,3] | permutations(2) }} => [[1,2],[1,3],[2,1],[2,3],[3,1],[3,2]] |
difference | 比较A,B数组差异 | {{ [1, 2, 5, 1, 3, 4, 10] | difference([1, 2, 3, 4, 5, 11, 99]) }} => [10] |
flatten | 扁平化列表 | {{ [1, 2, [3, [4, 5]], 6] | flatten(1) }} => [1,2,3,[4,5],6] {{ [1 , 2, [3, [4, 5]], 6] | flatten }} => [1,2,3,4,5,6] |
items2dict | 字典KV提取合并为新字典 | {{ [{‘key’: ‘hi’, ‘value’: ‘bye’}, {‘key’: ‘ciao’, ‘value’: ‘ciao’} ]| items2dict(‘key’,’value’)}} => { “hi”: “bye”, “ciao”: “ciao” } |
combinations | 穷举列表的所有元素的组合C | {{ [1,2,3,4] | combinations(3) }} => [[1,2,3],[1,2,4],[1,3,4],[2,3,4]] |
dict2items | 字典展开为对象列表 | {{ {‘a’: 1, ‘b’: 2} => [{ “key”: “a”, “value”: 1 }, { “key”: “b”, “value”: 2 }] |
zip | 组合列表元素(对多个列表并行递归,取遍历的对象生成元组) | {{ [1,2,3,4,5,6] | zip([‘a’,’b’,’c’,’d’,’e’,’f’]) }} => [[1,”a”],[2,”b”],[3,”c”],[4,”d”],[5,”e”],[6,”f”]] {{ [1,2,3] | zip([‘a’,’b’,’c’], [‘d’,’e’,’f’]) }} => [[1,”a”,”d”],[2,”b”,”e”],[3,”c”,”f”]] {{ [1,2,3] | zip([‘a’,’b’,’c’,’d’,’e’,’f’]) }} => [[1,”a”],[2,”b”],[3,”c”]] |
zip_longest | 将列表元素与填充符合并(同上,对取不到的值使用fillvalue 填充) |
{{ [1,2,3] | zip_longest([‘a’,’b’,’c’,’d’,’e’,’f’], [21, 22, 23], fillvalue=’X’) }} => [[1, “a”, 21], [2, “b”, 22], [3, “c”, 23], [“X”, “d”, “X”], [“X”, “e”, “X”], [“X”, “f”, “X”]] |
环境变量
链接 | 说明 | 示例 |
---|---|---|
expandvars | 返回补全环境变量的路径 | {{ ‘$HOME/stuff.txt’ | expandvars }} => ‘/home/myuser/stuff.txt’ |
expanduser | 返回一个带有~转换的路径 | {{ ‘~/stuff.txt’ | expanduser }} => ‘/home/myuser/stuff.txt’ |
Lookup 插件
用于主控机本地数据处理
query函数(简写q)也可以调用lookup插件,但query函数的默认返回一个列表
errors可以处理报错机制 ignore
,warn
,strict
(默认)
链接 | 说明 | 示例 |
---|---|---|
ini | 从ini文件中读取数据 section -节中查找键的位置 re -正则 false(默认) file -导入文件 |
{{ lookup(‘ini’, ‘键名’, section=’节点名’, file=’users.ini’) }} |
dict | 返回字典中的键/值对项 | {{ lookup(‘dict’, users) }} with_dict |
env | 读取环境变量的值 | {{ lookup(‘env’, ‘HOME’) }} {{ lookup(‘env’, ‘PATH’) }} |
file | 读取文件内容 | {{ lookup(‘file’, ‘/etc/hostname’) }} with_file |
fileglob | 列出符合模式的文件 | {{ lookup(‘fileglob’, ‘/etc/host*’) }} with_fileglob |
first_found | 返回files 列表中找到的第一个本地存在的文件 skip false(默认)抛出异常 true-当没有文件匹配时返回一个空列表 path -要在其中查找文件的路径列表(优先遍历) files -待查找的文件名列表 |
{{ lookup(‘first_found’, params) }} 建议使用模块内vars传参 |
indexed_items | 重写列表以返回“索引项” | with_indexed_items |
items | 转为对象列表 | with_items |
list | 简单地返回给定的内容 | with_list |
lines | 从命令中读取行 | with_lines: cat /etc/hostname |
nested | 使用其他列表的嵌套元素组成列表 | with_nested |
password | 检索或生成存储在文件中的随机密码 encrypt –md5_crypt bcrypt sha256_crypt sha512_crypt length -20(默认) |
{{ lookup(‘password’, ‘123’,length=30) }} => ‘IaaKoWqqTeEK5Of0SQ:sKqCzIsjdZL’ |
pipe | 从命令中读取输出 | {{ lookup(‘pipe’, ‘date’) }} => ‘2023年 04月 07日 星期五 15:51:37 CST’ |
random_choice | 从列表返回随机元素 | with_random_choice |
sequence | 根据数字序列生成一个列表 start -0(默认) count -0(默认) end -0(默认) format -格式化 stride -序号之间递增 1(默认) |
with_sequence: start=0 end=10 format=%02d with_sequence: start=10 end=0 stride=-1 |
subelements | 遍历字典列表中的嵌套键 | {{ q(‘subelements’, users, ‘groups’, {‘skip_missing’: True}) }} |
template | 使用Jinja2模板后检索文件内容 详细配置 | {{ lookup(‘template’, ‘./some_template.j2′, variable_start_string='[%’, variable_end_string=’%]’) }} |
together | 将列表合并为同步列表 | with_together |
url | 从 URL 返回内容 | detail |
5. 剧本
#检查脚本
ansible-playbook -C test.yml
#运行脚本
ansible-playbook test.yml
YAML格式
https://www.runoob.com/w3cnote/yaml-intro.html
需要固定格式对齐(不会报错,但会执行失败)(请不要手动tab,对齐为2个空格)
https://www.bejson.com/validators/yaml_editor/
建议使用编辑器 Sublime Text 或 VSCode,并安装ansible插件
YAML块运算符
>
折叠块运算符
key: >
This text
has multiple
lines
|
文本块运算符
key: |
This text
has multiple
lines
可用于多行shell脚本
- name: iterate user groups
shell: |
groupmod -o -g {{ item['guid'] }} {{ item['username'] }}
do_some_stuff_here
and_some_other_stuff
with_items: "{{ users }}"
Playbook 基本结构
- name: 剧本1
hosts: 主机清单中定义的组名
vars:
参数名: 参数值
vars_files: 外置参数文件路径
# gather_facts: false #关闭facts收集
tasks:
- name: 任务1
ignore_errors: true # 忽略错误并继续
任务模块:
模块参数名: 参数值
tags: 标签名
- name: 任务2
handles:
- name: 触发器1
任务模块:
模块参数名: 参数值
- name: 触发器2
任务模块:
模块参数名: 参数值
- name: 剧本2
hosts: 主机清单中定义的组名
基础字段详解
1. vars_* 变量定义及使用
vars 参数
- vars:
download_path: /opt/download/
#使用
- hosts: test
vars:
download_path: /opt/download/
tasks:
- name: 输出变量
debug:
msg: "{{download_path}}"
#变量在开头时需要加"",中间可忽略
vars_files 变量文件
将所有变量写入yml文件,顶格书写
vim vars1.yml
download_path: /opt/download/
env_path: /opt/env/
soft_path: /opt/soft/
- hosts: test
vars_files: ./vars1.yml
tasks:
- name: 输出变量
debug:
msg: |
下载路径->{{download_path}}
环境路径->{{env_path}}
软件路径->{{soft_path}}
group_vars 根据主机清单分组自动识别变量
./group_vars/主机名/vars.yml
优先
./group_vars/all/vars.yml
所有主机共用
- hosts: gp-master
#gather_facts: false #关闭收集
tasks:
- name: 输出变量
debug:
msg: "{{port}}"
- hosts: gp-datenode01
tasks:
- name: 输出变量
debug:
msg: "{{port}}"
- hosts: gp-datenode02
tasks:
- name: 输出变量
debug:
msg: "{{port}}"
- hosts: all
tasks:
- name: 输出变量
debug:
msg: "{{port}}"
#group_vars 结构树
group_vars
├── gp-datenode01
│ └── vars.yml
├── gp-datenode02
│ └── vars.yml
├── gp-datenode03
│ └── vars.yml
├── gp-master
│ └── vars.yml
└── vars.yml
#输出
PLAY [gp-datenode02] ***********************************************************
TASK [输出变量] ****************************************************************
ok: [192.168.1.182] => {
"msg": 7001
}
#all
PLAY [all] *********************************************************************
TASK [输出变量] ****************************************************************
ok: [192.168.1.183] => {
"msg": 8000
}
ok: [192.168.1.180] => {
"msg": 7000
}
ok: [192.168.1.182] => {
"msg": 7001
}
facts 主机内置变量
变量名 | 说明 |
---|---|
ansible_hostname | 主机名 |
ansible_memfree_mb | 内存剩余大小(MB) |
ansible_memtotal_mb | 内存总大小(MB) |
ansible_selinux.status | selinux开启状态 “disabled” |
ansible_swaptotal_mb | swap大小 |
ansible_architecture | 系统位数 “x86_64” |
ansible_distribution | 系统名 |
ansible_distribution_version | 系统版本 |
ansible_processor_cores | cpu核心数 |
ansible_processor_vcpus | cpu数量 |
ansible_processor_threads_per_core | cpu每核线程数 |
ansible_env | 环境变量 |
//输出 ansible test -m setup
{
"ansible_facts": {
"ansible_all_ipv4_addresses": [ "192.168.1.180", "172.17.0.1", "192.168.122.1" ],
"ansible_all_ipv6_addresses": [ "240e:37b:1bf3:3300:2ef0:5dff:fe43:376e", "fe80::2ef0:5dff:fe43:376e" ],
"ansible_apparmor": {
"status": "disabled"
},
"ansible_architecture": "x86_64",
"ansible_bios_date": "01/07/2020",
"ansible_bios_vendor": "American Megatrends Inc.",
"ansible_bios_version": "1.40",
"ansible_board_asset_tag": "Default string",
"ansible_board_name": "B365M PRO-VH (MS-7C31)",
"ansible_board_serial": "K4F0045483",
"ansible_board_vendor": "Micro-Star International Co., Ltd.",
"ansible_board_version": "1.0",
"ansible_chassis_asset_tag": "Default string",
"ansible_chassis_serial": "Default string",
"ansible_chassis_vendor": "Micro-Star International Co., Ltd.",
"ansible_chassis_version": "1.0",
"ansible_cmdline": {
"BOOT_IMAGE": "/vmlinuz-3.10.0-1160.83.1.el7.x86_64",
"LANG": "zh_CN.UTF-8",
"crashkernel": "auto",
"nouveau.modeset": "0",
"quiet": true,
"rd.driver.blacklist": "nouveau",
"rd.lvm.lv": "centos/swap",
"rhgb": true,
"ro": true,
"root": "/dev/mapper/centos-root",
"spectre_v2": "retpoline"
},
"ansible_date_time": {
"date": "2023-03-01",
"day": "01",
"epoch": "1677633367",
"epoch_int": "1677633367",
"hour": "09",
"iso8601": "2023-03-01T01:16:07Z",
"iso8601_basic": "20230301T091607666726",
"iso8601_basic_short": "20230301T091607",
"iso8601_micro": "2023-03-01T01:16:07.666726Z",
"minute": "16",
"month": "03",
"second": "07",
"time": "09:16:07",
"tz": "CST",
"tz_dst": "CST",
"tz_offset": "+0800",
"weekday": "星期三",
"weekday_number": "3",
"weeknumber": "09",
"year": "2023"
},
"ansible_default_ipv4": {
"address": "192.168.1.180",
"alias": "enp2s0",
"broadcast": "192.168.1.255",
"gateway": "192.168.1.1",
"interface": "enp2s0",
"macaddress": "2c:f0:5d:43:37:6e",
"mtu": 1500,
"netmask": "255.255.255.0",
"network": "192.168.1.0",
"prefix": "24",
"type": "ether"
},
"ansible_default_ipv6": {
"address": "240e:37b:1bf3:3300:2ef0:5dff:fe43:376e",
"gateway": "fe80::1",
"interface": "enp2s0",
"macaddress": "2c:f0:5d:43:37:6e",
"mtu": 1500,
"prefix": "64",
"scope": "global",
"type": "ether"
},
"ansible_device_links": {},
"ansible_devices": {},
"ansible_distribution": "CentOS",
"ansible_distribution_file_parsed": true,
"ansible_distribution_file_path": "/etc/redhat-release",
"ansible_distribution_file_variety": "RedHat",
"ansible_distribution_major_version": "7",
"ansible_distribution_release": "Core",
"ansible_distribution_version": "7.9",
"ansible_dns": {
"nameservers": [ "8.8.8.8" ]
},
"ansible_domain": "",
"ansible_effective_group_id": 0,
"ansible_effective_user_id": 0,
"ansible_enp2s0": {},
"ansible_env": {
"CLION_VM_OPTIONS": "/opt/soft/idea-IU-212.5284.40/ja-netfilter-all/vmoptions/clion.vmoptions",
"DATAGRIP_VM_OPTIONS": "/opt/soft/idea-IU-212.5284.40/ja-netfilter-all/vmoptions/datagrip.vmoptions",
"GOLAND_VM_OPTIONS": "/opt/soft/idea-IU-212.5284.40/ja-netfilter-all/vmoptions/goland.vmoptions",
"HARDWARE_PLATFORM": "x86_64",
"HOME": "/root",
"IDEA_VM_OPTIONS": "/opt/soft/idea-IU-212.5284.40/ja-netfilter-all/vmoptions/idea.vmoptions",
"LANG": "zh_CN.UTF-8",
"LESSOPEN": "||/usr/bin/lesspipe.sh %s",
"LOGNAME": "root",
"LS_COLORS": "rs=0:di=38;5;27:ln=38;5;51:mh=44;38;5;15:pi=40;38;5;11:so=38;5;13:do=38;5;5:bd=48;5;232;38;5;11:cd=48;5;232;38;5;3:or=48;5;232;38;5;9:mi=05;48;5;232;38;5;15:su=48;5;196;38;5;15:sg=48;5;11;38;5;16:ca=48;5;196;38;5;226:tw=48;5;10;38;5;16:ow=48;5;10;38;5;21:st=48;5;21;38;5;15:ex=38;5;34:*.tar=38;5;9:*.tgz=38;5;9:*.arc=38;5;9:*.arj=38;5;9:*.taz=38;5;9:*.lha=38;5;9:*.lz4=38;5;9:*.lzh=38;5;9:*.lzma=38;5;9:*.tlz=38;5;9:*.txz=38;5;9:*.tzo=38;5;9:*.t7z=38;5;9:*.zip=38;5;9:*.z=38;5;9:*.Z=38;5;9:*.dz=38;5;9:*.gz=38;5;9:*.lrz=38;5;9:*.lz=38;5;9:*.lzo=38;5;9:*.xz=38;5;9:*.bz2=38;5;9:*.bz=38;5;9:*.tbz=38;5;9:*.tbz2=38;5;9:*.tz=38;5;9:*.deb=38;5;9:*.rpm=38;5;9:*.jar=38;5;9:*.war=38;5;9:*.ear=38;5;9:*.sar=38;5;9:*.rar=38;5;9:*.alz=38;5;9:*.ace=38;5;9:*.zoo=38;5;9:*.cpio=38;5;9:*.7z=38;5;9:*.rz=38;5;9:*.cab=38;5;9:*.jpg=38;5;13:*.jpeg=38;5;13:*.gif=38;5;13:*.bmp=38;5;13:*.pbm=38;5;13:*.pgm=38;5;13:*.ppm=38;5;13:*.tga=38;5;13:*.xbm=38;5;13:*.xpm=38;5;13:*.tif=38;5;13:*.tiff=38;5;13:*.png=38;5;13:*.svg=38;5;13:*.svgz=38;5;13:*.mng=38;5;13:*.pcx=38;5;13:*.mov=38;5;13:*.mpg=38;5;13:*.mpeg=38;5;13:*.m2v=38;5;13:*.mkv=38;5;13:*.webm=38;5;13:*.ogm=38;5;13:*.mp4=38;5;13:*.m4v=38;5;13:*.mp4v=38;5;13:*.vob=38;5;13:*.qt=38;5;13:*.nuv=38;5;13:*.wmv=38;5;13:*.asf=38;5;13:*.rm=38;5;13:*.rmvb=38;5;13:*.flc=38;5;13:*.avi=38;5;13:*.fli=38;5;13:*.flv=38;5;13:*.gl=38;5;13:*.dl=38;5;13:*.xcf=38;5;13:*.xwd=38;5;13:*.yuv=38;5;13:*.cgm=38;5;13:*.emf=38;5;13:*.axv=38;5;13:*.anx=38;5;13:*.ogv=38;5;13:*.ogx=38;5;13:*.aac=38;5;45:*.au=38;5;45:*.flac=38;5;45:*.mid=38;5;45:*.midi=38;5;45:*.mka=38;5;45:*.mp3=38;5;45:*.mpc=38;5;45:*.ogg=38;5;45:*.ra=38;5;45:*.wav=38;5;45:*.axa=38;5;45:*.oga=38;5;45:*.spx=38;5;45:*.xspf=38;5;45:",
"MAIL": "/var/mail/root",
"PATH": "/usr/lib64/ccache:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin",
"PHPSTORM_VM_OPTIONS": "/opt/soft/idea-IU-212.5284.40/ja-netfilter-all/vmoptions/phpstorm.vmoptions",
"PWD": "/root",
"PYCHARM_VM_OPTIONS": "/opt/soft/idea-IU-212.5284.40/ja-netfilter-all/vmoptions/pycharm.vmoptions",
"QT_GRAPHICSSYSTEM_CHECKED": "1",
"SHELL": "/bin/bash",
"SHLVL": "2",
"SSH_CLIENT": "192.168.1.180 13510 22",
"SSH_CONNECTION": "192.168.1.180 13510 192.168.1.180 22",
"SSH_TTY": "/dev/pts/4",
"TERM": "xterm-256color",
"USER": "root",
"WEBSTORM_VM_OPTIONS": "/opt/soft/idea-IU-212.5284.40/ja-netfilter-all/vmoptions/webstorm.vmoptions",
"XDG_DATA_DIRS": "/root/.local/share/flatpak/exports/share:/var/lib/flatpak/exports/share:/usr/local/share:/usr/share",
"XDG_RUNTIME_DIR": "/run/user/0",
"XDG_SESSION_ID": "1811",
"XMODIFIERS": "@im=ibus",
"_": "/usr/bin/python"
},
"ansible_fibre_channel_wwn": [],
"ansible_fips": false,
"ansible_form_factor": "Desktop",
"ansible_fqdn": "gp-datanode01",
"ansible_hostname": "gp-datanode01",
"ansible_hostnqn": "",
"ansible_interfaces": [ "enp2s0", "lo", "docker0", "virbr0-nic", "virbr0" ],
"ansible_is_chroot": false,
"ansible_iscsi_iqn": "iqn.1994-05.com.redhat:2959aa9543ae",
"ansible_kernel": "3.10.0-1160.83.1.el7.x86_64",
"ansible_kernel_version": "#1 SMP Wed Jan 25 16:41:43 UTC 2023",
"ansible_lo": {},
"ansible_local": {},
"ansible_lsb": {},
"ansible_lvm": {},
"ansible_machine": "x86_64",
"ansible_machine_id": "ee26cddaa16d4dc5b7b74475be2cde56",
"ansible_memfree_mb": 9436,
"ansible_memory_mb": {
"nocache": {
"free": 11547,
"used": 4283
},
"real": {
"free": 9436,
"total": 15830,
"used": 6394
},
"swap": {
"cached": 98,
"free": 52766,
"total": 54063,
"used": 1297
}
},
"ansible_memtotal_mb": 15830,
"ansible_mounts": [],
"ansible_nodename": "gp-datanode01",
"ansible_os_family": "RedHat",
"ansible_pkg_mgr": "yum",
"ansible_proc_cmdline": {
"BOOT_IMAGE": "/vmlinuz-3.10.0-1160.83.1.el7.x86_64",
"LANG": "zh_CN.UTF-8",
"crashkernel": "auto",
"nouveau.modeset": "0",
"quiet": true,
"rd.driver.blacklist": "nouveau",
"rd.lvm.lv": [ "centos/root", "centos/swap"],
"rhgb": true,
"ro": true,
"root": "/dev/mapper/centos-root",
"spectre_v2": "retpoline"
},
"ansible_processor": [
"0","GenuineIntel","Intel(R) Core(TM) i5-9500F CPU @ 3.00GHz",
"1","GenuineIntel","Intel(R) Core(TM) i5-9500F CPU @ 3.00GHz",
"2","GenuineIntel","Intel(R) Core(TM) i5-9500F CPU @ 3.00GHz",
"3","GenuineIntel","Intel(R) Core(TM) i5-9500F CPU @ 3.00GHz",
"4","GenuineIntel","Intel(R) Core(TM) i5-9500F CPU @ 3.00GHz",
"5","GenuineIntel","Intel(R) Core(TM) i5-9500F CPU @ 3.00GHz"
],
"ansible_processor_cores": 6,
"ansible_processor_count": 1,
"ansible_processor_nproc": 6,
"ansible_processor_threads_per_core": 1,
"ansible_processor_vcpus": 6,
"ansible_product_name": "MS-7C31",
"ansible_product_serial": "Default string",
"ansible_product_uuid": "ACCC6BD6-CA0C-BF1A-A0AD-2CF05D43376E",
"ansible_product_version": "1.0",
"ansible_python": {
"executable": "/usr/bin/python",
"has_sslcontext": true,
"type": "CPython",
"version": {
"major": 2,
"micro": 5,
"minor": 7,
"releaselevel": "final",
"serial": 0
},
"version_info": [ 2, 7, 5, "final", 0 ]
},
"ansible_python_version": "2.7.5",
"ansible_real_group_id": 0,
"ansible_real_user_id": 0,
"ansible_selinux": {
"status": "disabled"
},
"ansible_selinux_python_present": true,
"ansible_service_mgr": "systemd",
"ansible_ssh_host_key_ecdsa_public": "AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBEgfGf/mU+hxKcs4m2mOL17tbhib8hws4VCed/KYrRRc/VsVWZjfA9iNvGJzwf/ezo2L7XsmZnOcOpMVc80SNIw=",
"ansible_ssh_host_key_ecdsa_public_keytype": "ecdsa-sha2-nistp256",
"ansible_ssh_host_key_ed25519_public": "AAAAC3NzaC1lZDI1NTE5AAAAIKjWxOFkKC5d1ZWKbX0esy8vmGREoNRJncp6epHTJw7v",
"ansible_ssh_host_key_ed25519_public_keytype": "ssh-ed25519",
"ansible_ssh_host_key_rsa_public": "AAAAB3NzaC1yc2EAAAADAQABAAABAQDIsp37iSdPGf1I7yoJohoMA8gSP8J2jHA1wJbV83gvRxBcqP6XTPE13qcspqdYCUF0zPxoB81H1OwkLlrg85rc8rfdabp2ALfZEaw1TM+aF8/3TBDGHvqYpW9MDNAbxhCdRGbkhfzKPpguKNgkXFz5IKArmepsiIR1jDfJHZNqfxe3iqW4BKEfrz7FHqi5b/fxyWm0EGlcPHXlhw6kfJugDknuK2rcPZ3M+QGFtUKDWznv9RwDjVgJbhNXOcXZH+BypmikL6AfiYeBFPygYH+QjlsBFvZXTKbRZO8yTk/WPYQXI1t4quMtnY6H841huSkw1LSzDOAtjA4mavWjAhWv",
"ansible_ssh_host_key_rsa_public_keytype": "ssh-rsa",
"ansible_swapfree_mb": 52766,
"ansible_swaptotal_mb": 54063,
"ansible_system": "Linux",
"ansible_system_capabilities": [],
"ansible_system_capabilities_enforced": "True",
"ansible_system_vendor": "Micro-Star International Co., Ltd.",
"ansible_uptime_seconds": 173504,
"ansible_user_dir": "/root",
"ansible_user_gecos": "root",
"ansible_user_gid": 0,
"ansible_user_id": "root",
"ansible_user_shell": "/bin/bash",
"ansible_user_uid": 0,
"ansible_userspace_architecture": "x86_64",
"ansible_userspace_bits": "64",
"ansible_virbr0": {},
"ansible_virbr0_nic": {},
"ansible_virtualization_role": "host",
"ansible_virtualization_tech_guest": [],
"ansible_virtualization_tech_host": [
"kvm"
],
"ansible_virtualization_type": "kvm",
"discovered_interpreter_python": "/usr/bin/python",
"gather_subset": [
"all"
],
"module_setup": true
},
"changed": false
}
例子
- hosts: all
vars_files: ./vars1.yml
tasks:
- name: 输出变量
debug:
msg: facts变量 ip -> {{ansible_default_ipv4.address}}
register变量(寄存器)
- hosts: test
tasks:
- name: get date
command: date +%F
register: date
- name: 输出date
debug:
msg: "{{ date.stdout }}"
template模板(jinja2格式)
下载目录: {{download_path}}
环境目录: {{env_path}}
软件目录: {{soft_path}}
jinja2
- 列表 [item1,item2]
- 元组 {item1,item2}
- 字典 {key1,value1}
- 逻辑运算 and,or,not
- 比较操作 ,!=,>,>=,<,<=
- 算术运算 +,-,*,/,//,%,**
- 流表达式 for,if,when
变量判断
- defined:判断变量是否已定义,已定义则返回真
-
undefined:判断变量是否未定义,未定义则返回真
-
none:判断变量的值是否为空,如果变量已定义且值为空,则返回真
- when: var2 is undefined
2. handlers 触发器
https://docs.ansible.com/ansible/latest/playbook_guide/playbooks_handlers.html
handlers是在当前剧本的所有tasks运行完成后,再执行的
即 剧本1->handlers->剧本2->handlers
- name: 当nginx.conf更改时,调用重载配置(剧本1)
hosts: test
tasks:
- name: 更改conf
copy:
src: nginx.conf
dest: /etc/nginx/conf.d/
backup: true
notify:
- nginx reloaded
handlers:
- name: nginx reloaded
systemd:
name: nginx
state: reloaded
- name: 更改hello文件(剧本2)
hosts: test
tasks:
- name: 更改hello
copy:
src: hello.txt
dest: /tmp/hello.txt
notify:
- change
handlers:
- name: change
debug:
msg: "我不干净了"
Naming handlers
tasks:
- name: Restart everything
command: echo "this task will restart the web services"
notify: "restart web services"
handlers:
- name: Restart memcached
service:
name: memcached
state: restarted
listen: "restart web services"
- name: Restart apache
service:
name: apache
state: restarted
listen: "restart web services"
3. when 条件
https://docs.ansible.com/ansible/latest/playbook_guide/playbooks_conditionals.html#conditionals
https://zhuanlan.zhihu.com/p/587770251
可以与facts变量和register变量一起使用
- name: 给gp-master和gp-datanode01推送配置
hosts: all
tasks:
- name: push conf
copy:
content: "我是配置"
dest: '/tmp/test.conf'
when: (ansible_hostname is match("gp-master|gp-datanode01"))
- name: 匹配系统版本
hosts: all
tasks:
- name: Centos 7
debug:
msg: "Centos 7"
when:
- ansible_distribution == 'CentOS'
- ansible_distribution_major_version == '7'
4. with_* loop 循环
https://blog.csdn.net/qq_35887546/article/details/105251517
https://www.cnblogs.com/ccbloom/p/15508665.html
with_random_choice 随机获得列表中的一个值
https://docs.ansible.com/ansible/latest/playbook_guide/playbooks_loops.html#with-random-choice
with_items 循环读取并赋值给{{item}}
https://docs.ansible.com/ansible/latest/playbook_guide/playbooks_loops.html#with-items
- hosts: test
gather_facts: false
vars:
items:
- QL
- World
tasks:
- name: Hello with_items
debug:
msg: "Hello {{ item }}"
with_items:
- World
- QL
- name: Hello loop
debug:
msg: "Hello2 {{ item }}"
loop:
- World
- QL
- name: Hello loop {{items}}
debug:
msg: "Hello3 {{ item }}"
loop: "{{items}}"
- name: Hello loop ojb
debug:
msg: "name=> {{ item.name }} value=>{{item.value}}"
loop:
- { name : 'QL' , value : '666' }
- { name : 'QAQ' , value : '777'}
- { name : 'Q_Q' , value : '888'}
#result
TASK [Hello with_items] *********************************************************************************
ok: [192.168.1.180] => (item=World) => {
"msg": "Hello World"
}
ok: [192.168.1.180] => (item=QL) => {
"msg": "Hello QL"
}
TASK [Hello loop] ***************************************************************************************
ok: [192.168.1.180] => (item=World) => {
"msg": "Hello2 World"
}
ok: [192.168.1.180] => (item=QL) => {
"msg": "Hello2 QL"
}
TASK [Hello loop ['QL', 'World']] ***********************************************************************
ok: [192.168.1.180] => (item=QL) => {
"msg": "Hello3 QL"
}
ok: [192.168.1.180] => (item=World) => {
"msg": "Hello3 World"
}
loop 和 with_* 的区别
https://docs.ansible.com/ansible/latest/playbook_guide/playbooks_loops.html#comparing-loop-and-with
loop
https://docs.ansible.com/ansible/latest/playbook_guide/playbooks_loops.html#loops
#字典转items
- name: Using dict2items
ansible.builtin.debug:
msg: "{{ item.key }} - {{ item.value }}"
loop: "{{ tag_data | dict2items }}"
vars:
tag_data:
Environment: dev
Application: payment
#将输入值转化为列表
loop: "{{ query('inventory_hostnames', 'all') }}"
loop: "{{ lookup('inventory_hostnames', 'all', wantlist=True) }}"
#带下标
- name: Count our fruit
ansible.builtin.debug:
msg: "{{ item }} with index {{ my_idx }}"
loop:
- apple
- banana
- pear
loop_control:
index_var: my_idx
#带暂停
- name: Create servers, pause 3s before creating next
community.digitalocean.digital_ocean:
name: "{{ item }}"
state: present
loop:
- server1
- server2
loop_control:
pause: 3
5. tag 标签
https://docs.ansible.com/ansible/latest/playbook_guide/playbooks_tags.html#tags
#ansible-playbook 对于tags的指令
#跳过 不匹配这些值的 剧本和任务
--skip-tags
#仅运行 匹配的 剧本和任务
-t(--tags)
#列出剧本中所有的tags
--list-tags
#例
ansible-playbook echo_vars2.yml --list-tags
ansible-playbook echo_vars2.yml -t gp-master
ansible-playbook echo_vars2.yml --skip-tags gp-master
echo_vars2.yml
(须执行 group_vars
步骤)
- hosts: gp-master
gather_facts: false
tasks:
- name: gp-master 的端口
debug:
msg: "{{port}}"
tags: gp-master
- hosts: gp-datenode01
gather_facts: false
tasks:
- name: gp-datenode01 的端口
debug:
msg: "{{port}}"
tags: gp-datenode01
- hosts: gp-datenode02
gather_facts: false
tasks:
- name: gp-datenode02 的端口
debug:
msg: "{{port}}"
tags: gp-datenode02
- hosts: all
gather_facts: false
tasks:
- name: all 的端口
debug:
msg: "{{port}}"
tags: all
6. include_* import_* 包含/引入
include_* 为动态加载,即执行到再加载
import_* 为静态加载,即导入剧本时加载所有剧本内容(变量等)
https://blog.csdn.net/y1431096034/article/details/89211694
https://www.cnblogs.com/mauricewei/p/10054041.html
import_playbook
导入剧本
https://docs.ansible.com/ansible/latest/collections/ansible/builtin/import_playbook_module.html#ansible-builtin-import-playbook-module-import-a-playbook
include_tasks
动态加载剧本(在执行play之前才会加载自己变量)
https://docs.ansible.com/ansible/latest/collections/ansible/builtin/include_tasks_module.html#ansible-builtin-include-tasks-module-dynamically-include-a-task-list
import_tasks
静态加载剧本(在playbooks解析阶段将父task变量和子task变量全部读取并加载)
https://docs.ansible.com/ansible/latest/collections/ansible/builtin/import_tasks_module.html#ansible-builtin-import-tasks-module-import-a-task-list
include_role
加载角色并执行
https://docs.ansible.com/ansible/latest/collections/ansible/builtin/include_role_module.html#ansible-builtin-include-role-module-load-and-execute-a-role
import_role
导入角色到剧本中
https://docs.ansible.com/ansible/latest/collections/ansible/builtin/import_role_module.html#ansible-builtin-import-role-module-import-a-role-into-a-play
例子
- name: Include a play after another play
import_playbook: otherplays.yaml
- name: Run tasks/other.yaml instead of 'main'
include_role:
name: myrole
tasks_from: other
- name: Import task list in play
import_tasks:
file: stuff.yaml
- name: Apply tags to tasks within included file when using free-form
include_tasks: install.yml
- name: All Run Tasks
hosts: all
tasks:
- name: This fails because I'm inside a play already
import_playbook: stuff.yaml
- name: Apply conditional to all imported tasks
import_tasks: stuff.yaml
- name: Include task list in play
include_tasks:
file: stuff.yaml
7. roles
角色允许您根据已知的文件结构自动加载相关的变量,文件,任务,处理程序和其他Ansible工件. 按照角色对内容进行分组后,您可以轻松地重用它们并与其他用户共享它们
https://docs.ansible.com/ansible/latest/playbook_guide/playbooks_reuse_roles.html#roles
roles/
basic/ # 这个等级代表一个"role"
tasks/ #
main.yml # <-- 如果有必要,任务文件可以包括更小的文件
handlers/ #
main.yml # <-- handlers文件
templates/ # <-- 模板文件夹
ntp.conf.j2 # <------- (建议以.j2结尾)模板文件
files/ #
bar.txt # <-- 复制文件
foo.sh # <-- 脚本文件
vars/ #
main.yml # <-- 与此角色关联的变量
defaults/ #
main.yml # <-- 此角色的默认低优先级变量
meta/ #
main.yml # <-- 角色依赖关系
library/ # 角色还可以包括自定义模块
module_utils/ # 角色还可以包含自定义的module_utils
lookup_plugins/ # 或者其他类型的插件,比如本例中的查找
QL/ # 与上面的"basic"相同的结构,用于"QL"角色
QAQ/ # ""
QoQ/ # ""
role环境结构
mkdir ./roles
#新建角色名
mkdir ./roles/QL
cd ./roles/QL
#新建目录列表
#默认
mkdir tasks handlers templates files vars
#完整
mkdir tasks handlers templates files vars defaults meta library module_utils lookup_plugins
tree -F
└── QL/
├── defaults/
├── files/
├── handlers/
├── library/
├── lookup_plugins/
├── meta/
├── module_utils/
├── tasks/
├── templates/
└── vars/
例子
原始playbook脚本
- name: 更改hello文件
hosts: test
vars:
text: QL
tasks:
- name: 推送hello
template:
src: hello.j2
dest: /tmp/hello.txt
notify:
- change
handlers:
- name: hello.txt被改变
debug:
msg: "hello被改变了"
#Hello.j2
Hello {{text}}
改造
roles_test.yml
- name: roles脚本测试例子
hosts: all
gather_facts: false
roles:
- role: QL
/roles/QL/vars/main.yml
text: QL
/roles/QL/tasks/main.yml
- name: 更改hello
template:
src: hello.j2
dest: /tmp/hello.txt
notify:
- change
- name: debug模式下输出
debug:
msg: "{{ text }}"
when: debug is defined
/roles/QL/handlers/main.yml
- name: change
debug:
msg: "hello被改变了"
/roles/QL/templates/hello.j2
#Hello.j2
Hello {{text}}
目录结构
#tree -F
.
├── roles/
│ └── QL/
│ ├── files/
│ ├── handlers/
│ │ └── main.yml
│ ├── tasks/
│ │ └── main.yml
│ ├── templates/
│ │ └── hello.j2
│ └── vars/
└── roles_test.yml
执行
ansible-playbook roles_test.yml
#触发debug模式下输出text变量
ansible-playbook roles_test.yml -e "debug=1"
#手动修改text变量
ansible-playbook roles_test.yml -e "debug=1 text=QL"
8. become 权限
https://docs.ansible.com/ansible/latest/collections/ansible/builtin/index.html#become-plugins
https://docs.ansible.com/ansible/latest/plugins/become.html#become-plugin-list
9. failed_when
https://docs.ansible.com/ansible/latest/playbook_guide/playbooks_error_handling.html#defining-failure
10. register
Playbook 剧本例程
初始化环境
- name: Eclipse安装
hosts: all
vars:
# 下载路径
download_path: /opt/download
# 软件安装路径
soft_path: /opt/soft
# 环境路径
env_path: /opt/env
tasks:
- name: 初始化download目录
file:
path: "{{ item }}"
state: directory
loop:
- download_path
- soft_path
- env_path
Eclipse安装
- name: Eclipse安装
hosts: gp-master
tasks:
- name: download eclipse-java-2021-12
get_url:
url: 'https://archive.eclipse.org/technology/epp/downloads/release/2021-12/R/eclipse-java-2021-12-R-linux-gtk-x86_64.tar.gz'
dest: '/opt/download/eclipse-java-2021-12-R-linux-gtk-x86_64.tar.gz'
force: true
- name: unzip eclipse-java-2021-12
unarchive:
src: '/opt/download/eclipse-java-2021-12-R-linux-gtk-x86_64.tar.gz'
dest: /opt/soft/
remote_src: true
- name: download eclipse-java-2021-12
get_url:
url: 'https://ftp.yz.yamagata-u.ac.jp/pub/eclipse/technology/babel/babel_language_packs/latest/BabelLanguagePack-eclipse-zh_4.22.0.v20211218020001.zip'
dest: '/opt/download/BabelLanguagePack-eclipse-zh_4.22.0.v20211218020001.zip'
force: true
- name: unzip BabelLanguagePack-eclipse-zh_4.22.0
unarchive:
src: '/opt/download/BabelLanguagePack-eclipse-zh_4.22.0.v20211218020001.zip'
dest: /opt/soft/
remote_src: true
- name: add desktop link
copy:
content: |
[Desktop Entry]
Encoding=UTF-8
Name=eclipse
Comment=Eclipse
Exec=/opt/soft/eclipse/eclipse
Icon=/opt/soft/eclipse/icon.xpm
Terminal=false
StartupNotify=true
Type=Application
Categories=Application;Development;
dest: '~/桌面/Eclipse.desktop'
LibreOffice安装
- name: LibreOffice安装
hosts: gp-master
tasks:
- name: unzip 英文主程序
unarchive:
src: 'https://mirrors.cloud.tencent.com/libreoffice/libreoffice/stable/7.5.0/rpm/x86_64/LibreOffice_7.5.0_Linux_x86-64_rpm.tar.gz'
dest: /opt/download/
remote_src: true
- name: unzip 中文包
unarchive:
src: 'https://mirrors.cloud.tencent.com/libreoffice/libreoffice/stable/7.5.0/rpm/x86_64/LibreOffice_7.5.0_Linux_x86-64_rpm_langpack_zh-CN.tar.gz'
dest: /opt/download/
remote_src: true
- name: unzip 中文帮助doc
unarchive:
src: 'https://mirrors.cloud.tencent.com/libreoffice/libreoffice/stable/7.5.0/rpm/x86_64/LibreOffice_7.5.0_Linux_x86-64_rpm_helppack_zh-CN.tar.gz'
dest: /opt/download/
remote_src: true
- name: 安装主程序
shell: 'rpm -ivh --force --nodeps /opt/download/LibreOffice_7.5.0.3_Linux_x86-64_rpm/RPMS/*.rpm'
- name: 安装中文包
shell: 'rpm -ivh --force --nodeps /opt/download/LibreOffice_7.5.0.3_Linux_x86-64_rpm_langpack_zh-CN/RPMS/*.rpm'
- name: 安装中文doc
shell: 'rpm -ivh --force --nodeps /opt/download/LibreOffice_7.5.0.3_Linux_x86-64_rpm_helppack_zh-CN/RPMS/*.rpm'
- name: 清理环境
shell: rm -rf /opt/download/LibreOffice_*
Ansible-Lint 格式化
https://ansible-lint.readthedocs.io/
配置项列表
ansible-lint -v -T
# List of tags and rules they cover
command-shell: # Specific to use of command and shell modules
- command-instead-of-module
- command-instead-of-shell
- inline-env-var
- no-changed-when
- risky-shell-pipe
core: # Related to internal implementation of the linter
- internal-error
- load-failure
- parser-error
- syntax-check
- warning
- schema
deprecations: # Indicate use of features that are removed from Ansible
- deprecated-bare-vars
- deprecated-local-action
- deprecated-module
- no-jinja-when
- role-name
experimental: # Newly introduced rules, by default triggering only warnings
- warning
- args
- no-log-password
- only-builtins
- sanity
formatting: # Related to code-style
- yaml
- fqcn
- jinja
- key-order
- no-tabs
- playbook-extension
- risky-octal
idempotency: # Possible indication that consequent runs would produce different results
- latest
- no-changed-when
- package-latest
idiom: # Anti-pattern detected, likely to cause undesired behavior
- command-instead-of-module
- command-instead-of-shell
- empty-string-compare
- inline-env-var
- literal-compare
- loop-var-prefix
- name
- no-handler
- no-relative-paths
- run-once
- var-naming
metadata: # Invalid metadata, likely related to galaxy, collections or roles
- galaxy
- meta-incorrect
- meta-no-info
- meta-no-tags
- meta-runtime
- meta-video-links
- role-name
opt-in: # Rules that are not used unless manually added to `enable_list`
- empty-string-compare
- no-log-password
- no-prompting
- no-same-owner
- only-builtins
risk:
- no-free-form
security: # Rules related o potentially security issues, like exposing credentials
- no-log-password
syntax:
- args
- no-free-form
unpredictability: # Warn about code that might not work in a predictable way
- avoid-implicit
- ignore-errors
- partial-become
- risky-file-permissions
unskippable: # Indicate a fatal error that cannot be ignored or disabled
- load-failure
- syntax-check
yaml: # External linter which will also produce its own rule codes
- yaml
配置文件
VSCode中可如下配置
//使用默认basic配置
"ansible.validation.lint.arguments": "--profile=basic",
//启用自定义配置
"ansible.validation.lint.arguments": "-c /root/.config/ansible-lint.yml"
.config/ansible-lint.yml
#个人使用习惯
skip_list:
- fqcn
- yaml
- no-free-form
- ignore-errors
- name
- jinja[spacing]
- risky-file-permissions
- no-changed-when
- package-latest
- command-instead-of-shell
- deprecated-bare-vars
PS:
There appears to be both ‘k=v’ shorthand syntax and YAML in this task. Only one syntax may be used.
#Categories=Application;Development;
Categories='Application;Development;'
#或脚本对齐问题
以下操作不会正常使用
- shell: |
cat <<EOF
This is a test.
EOF
Ansible实际上会用前导空格呈现文本,这意味着shell永远不会在一行的开头找到字符串EOF. 您可以通过如下方式使用cmd参数来避免
- shell:
cmd: |
cat <<EOF
This is a test.
EOF