文章目录
- ansible
- 安装
- 配置
- ansible.cfg
- 命令与模块
- 相关模块
- setup模块
- playbook
- Facts
- 基本语法
- 变量
- 变量作用域
- 逻辑控制语句
- 重用
- 标签
- lookup访问数据
- 过滤器filter
- 条件测试
- lineinfile模块
- role
ansible是基于Python开发的自动化运维工具,集合了众多运维工具(puppet、chef、func、fabric)的优点,实现了批量系统配置、批量程序部署、批量运行命令等功能。
ansible
Ansible本身集成的非常丰富的模块可以实现各种管理任务,其自带模块超过上千个。
- 部署简单,只需在主控端部署Ansible环境,被控端无需做任何操作;
- 默认使用SSH协议对设备进行管理;
- 有大量常规运维操作模块,可实现日常绝大部分操作;
- 配置简单、功能强大、扩展性强;
- 支持API及自定义模块,可通过Python轻松扩展;
- 通过Playbooks来定制强大的配置、状态管理;
- 轻量级,无需在客户端安装agent,更新时,只需在操作机上进行一次更新即可;
- 提供一个功能强大、操作性强的Web管理界面和REST API接口——AWX平台。
安装
ansible-core Version | Control node Python | Managed node Python |
---|---|---|
2.11 | Python 2.7, Python 3.5 - 3.9 | |
2.12 | Python 3.8 - 3.10 | Python 2.6 - 2.7, Python 3.5 - 3.10 |
2.13 | Python 3.8 - 3.10 | Python 2.7, Python 3.5 - 3.10 |
2.14 | Python 3.9 - 3.11 | Python 2.7, Python 3.5 - 3.11 |
安装:
python3 -m pip install --user ansible# 指定版本
python3 -m pip install --user ansible-core==2.12.3# 更新
python3 -m pip install --upgrade --user ansible
使安装生效,把路径添加到path中:
# 安装到用户路径下
export PATH=/home/xugd/.local/bin/:$PATH# 通过env环境安装
source /opt/deepops/env/bin/activate
配置
ansible主配置文件所在目录/etc/ansible/
,可能会有:
ansible.cfg
:ansible的主配置文件hosts
:主机清单,即intentory清单roles
:ansible角色
ansible默认主机信息配置为/etc/ansible/hosts
下,修改目录下hosts设定主机:
IP+A/C
:192.168.0.10 ansible_ssh_user=“vagrant” ansible_ssh_pass=“vagrant”nickname+IP+A/C
:testserver ansible_ssh_host=192.168.0.10 ansible_ssh_user=“vagrant” ansible_ssh_pass=“vagrant”nickname+IP+Account+节点私有key
:testserver ansible_ssh_host=192.168.0.10 ansible_ssh_user=“vagrant” ansible_private_key_file=./vagrant/machines/default/virtual/private_keyIP
:192.168.0.10# 注:这种方式需要先用ssh-keygen命令(回车三次)生成管理主机的public key然后把key用命令ssh-copy-id -i 发送到节点主机上去才能起作用(本机的也要添加自己的公钥)ssh-copy-id -i "/home/vagrant/.ssh/id_rsa.pub" vagrant@192.168.0.10
ansible.cfg
Ansible的默认配置文件是/etc/ansible/ansible.cfg
;若工作目录中有ansible.cfg,则使用当前目录中的;通过ansible --version
可查看当前使用的各配置信息。
若获取的信息不全(如缺少ansible_interfaces),可能是:
- 配置了获取的网课,如
ansible_interfaces=eth0
,要修改为ansible_interfaces=*
; - 没有net-tools软件包:
apt-get install net-tools
; - gathering设定有误:修改为
gathering = implicit
来获取所有信息
gathering指定了Ansible在收集主机信息时使用的策略:
gathering = explicit
:表示Ansible只收集在playbook中明确指定的信息。gathering = implicit
:表示Ansible收集所有可用的信息,包括在playbook中未明确指定的信息。gathering = basic
:表示Ansible只收集基本的主机信息,例如主机名和操作系统类型。gathering = smart
:表示Ansible将根据主机的连接类型和操作系统类型来决定使用哪种信息收集策略。
配置文件由几个部分组成,每一个部分包含以键值对形式定义的设置;
default
[defaults]#inventory = /etc/ansible/hosts //定义Inventory
#library = /usr/share/my_modules/ //自定义lib库存放目录
#remote_tmp = ~/.ansible/tmp //临时文件远程主机存放目录
#local_tmp = ~/.ansible/tmp //临时文件本地存放目录
#gathering = smart // facts收集方式
#forks = 5 //默认开启的并发数
#poll_interval = 15 //默认轮询时间间隔
#sudo_user = root //默认sudo用户
#ask_sudo_pass = True //是否需要sudo密码
#ask_pass = True //是否需要密码
#host_key_checking = False //首次连接是否检查key认证
#roles_path = /etc/ansible/roles //默认下载的Roles存放的目录
#log_path = /var/log/ansible.log //执行日志存放目录
#module_name = command //默认执行的模块
#action_plugins = /usr/share/ansible/plugins/action //action插件存放目录
#callback_plugins = /usr/share/ansible/plugins/callback //callback插件存放目录
#connection_plugins = /usr/share/ansible/plugins/connection //connection插件存放目录
#lookup_plugins = /usr/share/ansible/plugins/lookup //lookup插件存放目录
#vars_plugins = /usr/share/ansible/plugins/vars //vars插件存放目录
#filter_plugins = /usr/share/ansible/plugins/filter //filter插件存放目录
#test_plugins = /usr/share/ansible/plugins/test //test插件存放目录
#strategy_plugins = /usr/share/ansible/plugins/strategy //strategy插件存放目录
#fact_caching = memory //getfact缓存的主机信息存放方式
#retry_files_enabled = False
#retry_files_save_path = ~/.ansible-retry //错误重启文件存放目录
privilege_escalation
[privilege_escalation]
#become=True //是否sudo
#become_method=sudo //sudo方式
#become_user=root //sudo后变为root用户
#become_ask_pass=False //sudo后是否验证密码
ssh_connection
[ssh_connection]
#pipelining = False //管道加速功能,需配和requiretty使用方可生效
命令与模块
ansible命令选项:
-i
:inventory list,指定主机清单;-m
:modle,指定模块;-a
:action,指定动作(一般是模块内的选项);
ansible localhost -m debug -a "var=hostvars"# 查看本机facts
ansible localhost -m setup
主机清单文件:
[web]
192.169.1.10 ansible_ssh_port=22 ansible_ssh_user=test ansible_ssh_pass='123'[db]
192.169.1.20
192.169.1.21[nfs]
192.169.1.30# 创建组(通过添加children)
[data:children]
db
nfs # 设定组内所有主机的参数
[db:vars]
ansible_ssh_port=22
ansible_ssh_user=test
ansible_ssh_pass='123'
ansible -i hosts data -m shell -a 'hostname'# hosts文件中web分组内的主机
ansible -i hosts web -m ping
相关模块
模块说明与索引:https://docs.ansible.com/ansible/latest/collections/ansible/index.html
# 查询模块(参数)
ansible-doc -s {model}#
ansible-doc -l
模块:
- ping:检查与主机的连通性;
- shell:执行shell命令;会经过远程主机上的/bin/bash处理;
- command:(默认模块)执行简单命令,不支持特殊符号;
- 不会经过远程主机的/bin/bash处理;
- 命令中不能含有重定向、管道符等操作符;如 “<” , “>”, “|”, “;” 和 "&"等(此时需要使用shell模块);
- 远程节点是windows系统,则需要使用win_command模块;
- script:在远程主机上执行ansible管理主机上的脚本;
- file:文件创建、删除等;
- copy:复制文件到远程主机;
setup模块
收集指定服务器的信息,每个被管理节点在接收并运行管理命令之前,会将自己主机相关信息,如操作系统版本、IP地址等报告给远程的ansbile主机。
ansible all -m setup -a "filter=ansible_os_family"
filter常用可选项:
- ansible_all_ipv4_addresses:仅显示ipv4的信息
- ansible_devices:仅显示磁盘设备信息
- ansible_distribution:显示是什么系统,例:centos,suse等
- ansible_distribution_major_version:显示是系统主版本
- ansible_distribution_version:仅显示系统版本
- ansible_machine:显示系统类型,例:32位,还是64位
- ansible_eth0:仅显示eth0的信息
- ansible_hostname:仅显示主机名
- ansible_kernel:仅显示内核版本
- ansible_lvm:显示lvm相关信息
- ansible_memtotal_mb:显示系统总内存
- ansible_memfree_mb:显示可用系统内存
- ansible_memory_mb:详细显示内存情况
- ansible_swaptotal_mb:显示总的swap内存
- ansible_swapfree_mb:显示swap内存的可用内存
- ansible_mounts:显示系统磁盘挂载情况
- ansible_processor:显示cpu个数(具体显示每个cpu的型号)
- ansible_processor_vcpus:显示cpu个数(只显示总的个数)
- ansible_python_version:显示python版本
playbook
playbook是ansible的脚本语言(yaml格式);
# --verbose可输出细节;-f指定脚本执行并行度
ansible-playbook deploy.yml [--verbose] [-f 10]# 查看脚本影响的主机
ansible-playbook deploy.yml --list-hosts
Facts
ansible通过setup模块搜集主机的系统信息(叫做Facts);每个playbook执行前默认会执行setup模块,所以这些Facts信息可以直接以变量形式使用。
# 获取hosts中主机的setup信息
ansible -i hosts all -m setup# 在yaml中直接引用
{{ ansible_os_family }} # 操作系统名称
{{ ansible_eth2.ipv4.address }}
搜集facts比较好使,若不需要可在playbook中关闭:
- hosts: all gather_facts: no
基本语法
基本的playbook脚本包括:
- 在什么机器上以什么身份执行;
- hosts:主机ip、组名
- users:远程执行时的身份
- 执行的任务是什么;
- tasks:
- 善后任务有什么;
- handlers
任务列表:
- 任务是从下至下顺序执行的,若中间出错则整个playbook会中值;
- 每个任务都是对模块的一次调用,只是参数与变量不同而已;
- 每个任务最后有name熟悉(供人阅读);
tasks:
- name: check apache runningservice: name=httpd state=runningnotify:- handler-test
- name: copy file to clientcopy:src: /etc/ansible/hostsdest: /etc/ansible/hostsowner: rootgroup: rootmode: 0644notify:- handler-test handlers:
- name: handler-testdebug: msg="call in action, only excute once"
响应事件handler:handler是在任务中被调用的(执行状态为changed时),一个handler最多被执行一次(所有调用其的任务都执行完成后执行)。
handler按照定义顺序(而非调用顺序)执行。
变量
playbook中变量包括:
- 用户自定义的变量;
- ansible搜集的远程节点系统信息的变量;
- 任务执行结果会注册为变量;
- 执行playbook时传入的变量(额外变量)
引用变量通过{{ var }}
,若{
出现在值开始位置,可能yaml会报错,此时需添加引号"{{ var }}"
。
自定义变量:
通过vars在脚本中定义
- hosts: "{{ hostlist | default('all') }}"vars:http_port: 80# vars_files:# - vars/server.yamltasks:- name: add firewalld rulefirewalld: port={{ http_port }}/tcp permanent=true state=enabled immediate=yes
变量较多时,可放在单独的文件中,通过"var_files"引入;vars/server.yaml内容:
http_port: 80
结果注册为变量:通过register把任务的执行结果注册为一个变量
- hosts: "{{ hostlist | default('all') }}" tasks: - shell: lsregister: resultignore_errors: True- shell: echo "{{ result.stdout }}"when: result.rc == 5- debug: msg="{{ result.stdout }}"
通过命令传递参数:
ansible-playbook deploy.yml -e "hostlist=web user=root"# 通过参数配置文件传递
ansible-playbook deploy.yml --extra-vars "@vars.json"
变量作用域
role default变量优先级最低,其他变量作用域越小优先级越高:
role default变量是存放在roles/{x}/defaults/main.yml
文件中的:
---
http_port: 80
inventory变量是存放在hosts文件(默认/etc/ansible/hosts
)中的:
host1 ansible_port=5555 ntp_server=first.ntp.com[all:vars]
ntp_server=all.ntp.com
vars_prompt是用户在执行playbook时输入的变量:
- hosts: allvars_prompt:- name: "name"prompt: "what is your name?"
逻辑控制语句
逻辑控制语句有:
- when:条件判断语句
- loop:循环语句
- block:把几个任务组成代码块;
when类似编程语言中的if,只有条件满足时才执行指定步骤;
|int
:转换类型为int;not foo
:foo为假时;food is [not] defined
:foo定义了时;'OK' in output
: outpu中包含子串时;
tasks:
- name: "shut down debian"command: /sbin/shutdown -t nowwhen: ansible_os_family == "Debian" and ansible_lsb.major_release|int >= 16
循环:
通过with_itmes可实现最简单的循环:
- name: add usersuser: name={{ item }} state=present groups=wheelwith_items:- one- two# with_items: "{{ user_list }}" # user_list变量为["one", "two"]# 嵌套(内部还有list)with_nested:- ['one', 'two'] # {{ item[0] }}访问第一个- ['client', 'server']# 匹配单个目录中的文件with_fileglob:- /files/*
block块:
tasks:
- block:- template: src=src.j2 dest=/etc/foo.conf- service: name=bar state=started enabled=Truewhen: ansible_distribution == "Ubuntu"become: truebecome_user: root
重用
通过包含已有文件,来重用:
- include语句:重用单个playbook脚本
- role语句:重用实现特定功能的playbook文件;
inclue语句:
tasks:
# 可传递参数,多个参数用空格分割
- include: firewall.yml port=80
- include: default.yaml---
# 全局include,不能传递参数
- include: slurm.yaml
标签
标签tags用于标识任务,可根据需要选择执行指定的;但有特殊的标签
- always:除非明确指定不执行否则始终执行
- hosts: 127.0.0.1 gather_facts: notasks: - shell: lsname: ls filestags: [first]- shell: pwdname: pwd dirtags: [second]- shell: hostnamename: no-tag- debug: msg="always print out"tags:- always
ansible-playbook test.yml --tags "second"
# 只有pwd与debug会被执行
- tagged:执行所有设定了标签的任务
- untagged:执行所有未设定标签的任务
lookup访问数据
lookup能读取ansible管理节点上文件系统的文件内容、或数据库中的内容到ansible变量中。
- hosts: allvars: # 设定变量# 读取全部文件内容到contents- contents: "{{ lookup('file', 'data/plain.txt') }}"# 如pass文件存在,则读取到password中;否则先生成5位的随机密码- password: "{{ lookup('password', '/tmp/pass lenght=5') }}"# 读取环境变量- env-home: "{{ lookup('env', 'HOME') }}"# 获取命令执行结果- today: "{{ lookup('pipe', 'date') }}"tasks: # 直接使用变量# 读取tmplate文件(变量替换后的内容;若有未定义的变量,则报错)- debug: msg="contents: {{ lookup('template', 'test.j2') }}# 读取ini文件(name为user段下的键名,name=mike)- debug: msg="user name is: {{ lookup('ini', 'name section=user file=users.ini')}}"# 读取csv文件指定单元- debug: msg="user name is: {{ lookup('csvfile', 'name file=test.csv delimiter=, col=1') }}"# 查询dns- debug: msg="IPs of baidu: {{ lookup('dig', 'baidu.com.') }}"
ini类型(或java的propterites文件)的参数格式为:
lookup('ini', '{key} [type=properties|ini] [section={name}] [file={file}] [re=true] [default={value}]
参数 | 默认 | 说明 |
---|---|---|
default | “” | key不存在时的返回值 |
encoding | “utf-8” | 文件的编码格式 |
file | “ansible.ini” | 要加载的文件 |
re | “no” | 标识key是否使用正则表达式匹配 |
section | “global” | 在哪个Section里查找key |
type | ini | 文件类型ini/properties |
csv文件的参数格式:
lookup('csvfile', '{key} {arg}={val})
其中{key}为第一行(标题)中单元格的值,其他参数为可选的:
参数 | 说明 |
---|---|
col | 输出的列(从0开始),默认1 |
default | key不存在时的返回值,默认空串 |
delimiter | 分隔符,默认"TAB" |
encoding | 文件的编码格式,默认"utf-8" |
file | 要加载的文件,默认"ansible.csv" |
过滤器filter
ansible提供了内置过滤器,也可使用模板语言Jinja2的过滤器功能。
- hosts: all vars: # 设定变量test_str: "this is a test"test_bool: Truetasks:# quote给字符串加引号- debug: msg="echo {{ test_str | quote }}"# default为未定义的变量提供默认值- debug: msg="{{ undef_var | default('def-values') }}"# 判断变量是否为bool值- debug: msg="check is bool"when: test_bool | bool# ternary,三目运算符;前面结果为真时返回第一个值,否则返回第二个值- debug: msg="{{ (sex == "M") | ternary('Mr', 'Ms') }}# 组合操作- debug: msg="list {{ [1,2,3,2,4,5,4] | unique | shuffle | join(',') }}"
过滤器提供了对文件的操作,linux下路径操作过滤器:
- basename:获取文件名;
- dirname:获取目录;
- expanduser:扩展
~
为实际路径; - realpath:获取连接指向文件的实际路径;
- relpath:获取相对某一目录的相对路径;
- splitext:分割文件名,返回(name, ext)元组;
过滤器提供的对字符串操作:
- quote:给字符串加引号;
- base64:得到字符串的base64编码
- b64encode:编码
- b64decode:解码
- to_uuid:获取uuid
- hash:字符串的hash值
- hash(‘sha1’)
- hash(‘md5’)
- hash(‘sha256’)
- password_hash(‘sha256’)
- password_hash(‘sha512’, ‘my-salt’)
- comment: 把字符串变成注释
- comment:使用#注释
- comment(‘c’):使用
//
注释 - comment(‘plain’, prefix=‘##**##’, postfix=‘####’):指定注释
- regex:用正则表达式对字符串进行替换
- regex_replace(‘^a.*$’, ‘a\1’)
- regex_escape()
- 判断字符串是否是合法IP
- ipaddr
- ipv4
- ipv6
- ipaddr(‘address’)
- 转换为时间戳(格式为:'%Y-%m-%d %H:%M:%S’的)
- to_datetime
过滤器提供的对JSON的操作:
- format:按照JSON/YAML格式输出:
- to_json/to_nice_json
- to_yaml/to_nice_yaml
- query:搜索符合条件的属性(返回属性数组)
- json_query(‘cluster[*].name’)
- combine:合并两个json对象
过滤器提供的对数据结构的操作:
- random:取随机数(既可从列表中随机取元素,也可生成随机数)
[1,2,3] | random
:从列表中随机抽取一个元素59|random
:不超过59的随机数100|random(2, 10)
:提供起始值与步长(只能是02、12…)
- 对List的操作:
- min:取最小值;
- max:取最大值;
- join:所有元素连接成字符串;
- shuffle:洗牌(乱序生成一个新的List);
- map:对list映射操作;
- 对Set的操作:
- unique:去重;
- union:交集;
- difference:差集;
- symmetric_difference:对称差集;
条件测试
ansible提供了内置测试功能,也可使用模板语言Jinja2的测试功能。
- hosts: all vars: # 设定变量test_url: "http://example.com/bar/foo"test_ver: "10.2"tasks:- debug: "msg='matched'"when: test_url | match("http://.*.com/bar/foo")- debug: "msg='searched'"when: test_url | search(".*/bar/")# 版本比较- debug: msg="{{ test_ver | version_compare('9.0', '>=')}}"- debug: msg="version checked"when: test_ver | version_compare('13.1', operator='lt', strict=True)
字符串测试(使用match与search)
版本比较(version_compare)
List的包含关系测试:
a | issuperset(b)
:a包含b;a | issubset(b)
:b包含a;
文件路径测试:
- is_dir:是目录
- is_file:是文件
- is_link:是连接
- exists:存在
判断执行结果(以register: result
为例):
- failed:失败
- changed:改变
- succeeded:成功
- skipped:跳过
lineinfile模块
lineinfile是针对文件中行内容的操作(想要改变文件中相似的多行,可以使用replace模块;想要插入/更新/删除一个行块,可以使用blockinfile模块)。
名称 | 必选 | 默认值 | 可选值 | 备注 |
---|---|---|---|---|
backrefs | no | no | yes/no | 若regexp没有匹配到行:为no时则再文件尾添加一行;为yes时不做任何修改 |
backup | no | no | yes/no | 创建一个包含时间戳信息的备份文件;以便在需要时能够找回原始的文件 |
create | no | no | yes/no | 与state=present一起使用。如果指定了这个参数,当要修改的文件不存在的时候,会创建它;否则会报错。 |
firstmatch | no | no | yes/no | 为yes时,表示insertafter、insertbefore插入到匹配的第一行前或后 |
group | no | 设置文件/目录的所属组 | ||
insertafter | no | EOF | EOF/regex | 插入regex最后一个匹配行的后面;没有匹配则不插入。若为EOF,则插入文件尾 |
insertbefore | no | BOF/regex | 插入regex最后一个匹配行的后面;没有匹配则不插入。若为BOF,则插入文件首 | |
line | no | 要插入或者替换的行 | ||
mode | no | 设置文件权限(如0644);也可指定为符号模式(例如u+rwx或u=rw,g=r,o=r) | ||
others | no | file模块的其他参数可以在这里使用 | ||
owner | no | 设置文件/目录的所属用户 | ||
path | yes | 要修改的文件,也可以使用dest,destfile,name | ||
regexp | no | 用于匹配的正则表达式 | ||
state | no | present | present/absent | present时新增或替换一行,absent时删除行 |
unsafe_writes | no | yes/no | 是否以不安全的方式进行,可能导致数据损坏 | |
validate | no | None | 保存修改之前进行校验("%s"表示修改的文件) |
- name: Update host entry in hosts filehosts: allbecome: truetasks:- name: Update host entrylineinfile:path: /etc/hostsregexp: "^192.168.1.10"line: "192.168.1.100 data100"backrefs: yes
role
role比include强大灵活,可分享一个文件夹(包含完整功能的所有文件);roles路径查找(默认为/etc/ansible/roles):
- 当前目录下的roles目录;
- 环境变量
ANSIBLE_ROLES_PATH
; - 配置文件中
roles_path
设定的目录(当前目录下的ansible.cfg,home下的.ansible.cfg,/etc/ansible/ansible.cfg
);
- hosts: all remote_user: rootroles:- simple # 查找simple role并执行里面的tasks# 带参数的role- role: role_with_paramvars:ex_param: "param from out ex"# 带条件的role- role: role_with_whenwhen: "ansible_os_family == 'Ubuntu'"
role语句:
ansible_playbooks/
└── roles 必须叫roles└── websrvs -------------role名称├── defaults│ └── main.yml├── files│ ├── mysql.tar.gz│ └── nginx.tar.gz├── handlers│ └── main.yml├── meta├── tasks│ ├── install.yml│ └── main.yml├── templates│ └── nginx.conf.j2└── vars
目录结构说明(不需要包含所有目录,但tasks下的main是必须的):
目录名称 | 目录说明 |
---|---|
files | 用来存放由copy模块或script模块调用的文件 |
templates | 用来存放模板,templat 模块会自动在此目录中寻找模板文件 |
tasks | 此目录应当包含一个main.yml文件,用于定义任务列表,可以使用include包含此目录下的其他task文件 |
handlers | 此目录应当包含一个main.yml文件,用于定义此角色中触发条件时执行的动作 |
vars | 此目录应当包含一个main.yml文件,用于定义此角色用到的变量 |
defaults | 此目录应当包含一个main.yml文件,用于为当前角色设定默认变量 |
meta | 此目录应当包含一个main.yml文件,用于定义此角色的特殊设定及其依赖关系 |