Ansible
Ansible
Github 地址:GitHub - ansible/ansible
官网:ansible
官方文档:Ansible Community
中文官方文档:Ansible中文权威指南 - 国内最专业的Ansible中文官方学习手册
参考博客:
一套比较完整的学习笔记,1. Ansible 介绍 - My Docs
安装
不建议通过 docker 中安装 ansible 使用 ansible,最好直接在本地安装 ansible,因为我们在 ansible 进行远程操作的时候,可能会出现远程卸载 docker 的操作,同时,ansible 也是可以管理本机的,那如果需要操作本机卸载 docker,那就可能把自己也卸载掉,所以,我们尽量不要让 ansible 依赖别的工具。
在线安装
CentOS7安装Ansible - syushin - 博客园
ansible 官方没有跟 docker 一样提供官方的的 rpm 包,因此只能自己这样手动打包离线安装包:yum获取rpm软件包的三种方法_yum rpm包_feike_3的博客-CSDN博客
把在线安装的
yum install -y ansible
替换成
yum install --downloadonly --downloaddir=/opt/ansiblepackage -y ansible
我们可以注意到,下载下来的,基本上就是一个
ansible-2.9.27-1.el7.noarch.rpm
加一堆python-
开头的包
然后到 /opt/ansiblepackage
里拿到安装包,上传到离线服务器,然后在 rpm 所在目录下执行
rpm -ivh *.rpm
如果存在重复安装,则需要带上强制参数
rpm -ivh --force *.rpm
即可
卸载
以下两种方式都可以,推荐用 yum 的方式
(通过 rpm)
rpm -e ansible
(通过 yum)
yum remove -y ansible.noarch
配置文件
Ansible3: 配置文件管理 - breezey - 博客园
host 文件
Ansible hosts文件配置格式 - chengxuyonghu - 博客园
Ansible主机清单Inventory文件hosts | 小强斋太-Study Notes
ansible_ssh_host
将要连接的远程主机名.与你想要设定的主机的别名不同的话,可通过此变量设置.
ansible_ssh_port
ssh端口号.如果不是默认的端口号,通过此变量设置.
ansible_ssh_user
默认的 ssh 用户名
ansible_ssh_pass
ssh 密码(这种方式并不安全,我们强烈建议使用 --ask-pass 或 SSH 密钥)
ansible_sudo_pass
sudo 密码(这种方式并不安全,我们强烈建议使用 --ask-sudo-pass)
ansible_sudo_exe (new in version 1.8)
sudo 命令路径(适用于1.8及以上版本)
ansible_connection
与主机的连接类型.比如:local, ssh 或者 paramiko. Ansible 1.2 以前默认使用 paramiko.1.2 以后默认使用 'smart','smart' 方式会根据是否支持 ControlPersist, 来判断'ssh' 方式是否可行.
ansible_ssh_private_key_file
ssh 使用的私钥文件.适用于有多个密钥,而你不想使用 SSH 代理的情况.
ansible_shell_type
目标系统的shell类型.默认情况下,命令的执行使用 'sh' 语法,可设置为 'csh' 或 'fish'.
ansible_python_interpreter
目标主机的 python 路径.适用于的情况: 系统中有多个 Python, 或者命令路径不是"/usr/bin/python",比如 \*BSD, 或者 /usr/bin/python
不是 2.X 版本的 Python.我们不使用 "/usr/bin/env" 机制,因为这要求远程用户的路径设置正确,且要求 "python" 可执行程序名不可为 python以外的名字(实际有可能名为python26).
与 ansible_python_interpreter 的工作方式相同,可设定如 ruby 或 perl 的路径....
其中,服务器组名称不要使用 -
,会有告警。
# 服务器组名不要使用 - 会警告
[dept_node]
node_l1 ansible_ssh_host=10.8.65.190 ansible_ssh_user="root" ansible_ssh_pass="dameng777" ansible_ssh_port=22
node_l2_1 ansible_ssh_host=10.8.65.179 ansible_ssh_user="root" ansible_ssh_pass="dameng777" ansible_ssh_port=22
node_l2_2 ansible_ssh_host=10.8.65.193 ansible_ssh_user="root" ansible_ssh_pass="dameng777" ansible_ssh_port=22
# node_l3_1_1 ansible_ssh_host=192.168.0.10 ansible_ssh_user="root" ansible_ssh_pass="dameng777" ansible_ssh_port=22
# node_l3_1_2 ansible_ssh_host=192.168.0.10 ansible_ssh_user="root" ansible_ssh_pass="dameng777" ansible_ssh_port=22
# node_l3_2_1 ansible_ssh_host=192.168.0.10 ansible_ssh_user="root" ansible_ssh_pass="dameng777" ansible_ssh_port=22
# [monitor_node]
# [audit_node]
ansible 的 login shell 和 non-login shell
同样的一行脚本,直接在被管理机器上执行,或者通过 ssh 连上被管理机器执行,没问题,但是通过 ansible 的 shell 模块执行就有问题,这个时候就要考虑是不是 因为 non-login shell 造成了问题
关于 login shell 和 none-login shell,我们在《Bash 启动环境》和《Bash 启动时的配置文件加载》中有很详细的描述。
【ansible】关于ansible执行过程中加载环境变量问题 - 简书
https://bbs.huaweicloud.com/blogs/360180
ansible学习系列之become的使用_枫夜求索阁的技术博客_51CTO博客
配置文件ansible.cfg中Become (Privilege Escalation)_51CTO博客_ansible.cfg文件的作用
SH 远程执行脚本报错 "command not found",且环境变量不生效,执行 env 命令后发现打印出来的环境变量少了很多。
原因是之前将环境变量配置在了/etc/profile 中,但是 SSH 远程执行脚本时实际上执行的是 non-login shell,而 non-login shell 不会读取/etc/profile 配置文件。
简单来说,用 SSH 客户端(比如 Putty)登陆 Linux 系统时,要求输入用户名/密码登录或根据 SSH key 登录时,就是 login shell。
而在 A 机器上使用 SSH 免密码登录 B 机器,在 B 机器上执行 Shell 脚本,就是 non-login shell。
用 Ansible 在目标机器上远程执行 Shell 脚本时,也是 non-login shell,因为 Ansible 是基于 SSH 的。
ansible playbook 参数
Options:
--ask-vault-pass
#ask for vault password
#加密playbook文件时提示输入密码
-C, --check
#don't make any changes; instead, try to predict some of the changes that may occur
#模拟执行,不会真正在机器上执行(查看执行会产生什么变化)
-D, --diff
#when changing (small) files and templates, show the differences in those files; works great with --check
#当更新的文件数及内容较少时,该选项可显示这些文件不同的地方,该选项结合-C用会有较好的效果
-e EXTRA_VARS, --extra-vars=EXTRA_VARS
#set additional variables as key=value or YAML/JSON
#在Playbook中引入外部参数变量
--flush-cache
#clear the fact cache
#将fact清除到的远程主机缓存
--force-handlers
#run handlers even if a task fails
#强制运行handlers的任务,即使在任务失败的情况下
-f FORKS, --forks=FORKS
#specify number of parallel processes to use(default=5)
#并行任务数。FORKS被指定为一个整数,默认是5
-h, --help
#show this help message and exit
#打开帮助文档API
-i INVENTORY, --inventory-file=INVENTORY
#specify inventory host path (default=/etc/ansible/hosts) or comma separated host list.
#指定要读取的Inventory文件
-l SUBSET, --limit=SUBSET
#further limit selected hosts to an additional pattern
#限定执行的主机范围
--list-hosts
#outputs a list of matching hosts; does not execute anything else
#列出执行匹配到的主机,但并不会执行
--list-tags
#list all available tags
#列出所有可用的tags
--list-tasks
#list all tasks that would be executed
#列出所有即将被执行的任务
-M MODULE_PATH, --module-path=MODULE_PATH
#specify path(s) to module library (default=None)
#要执行的模块的路径
--new-vault-password-file=NEW_VAULT_PASSWORD_FILE
#new vault password file for rekey
#
--output=OUTPUT_FILE
#output file name for encrypt or decrypt; use - for stdout
#
--skip-tags=SKIP_TAGS
#only run plays and tasks whose tags do not match these values
#跳过指定的tags任务
--start-at-task=START_AT_TASK
#start the playbook at the task matching this name
#从第几条任务(START_AT_TASK)开始执行
--step
#one-step-at-a-time: confirm each task before running
#逐步执行Playbook定义的任务,并经人工确认后继续执行下一步任务
--syntax-check
#perform a syntax check on the playbook, but do not execute it
#检查Playbook中的语法书写,并不实际执行
-t TAGS, --tags=TAGS
#only run plays and tasks tagged with these values
#指定执行该tags的任务
--vault-password-file=VAULT_PASSWORD_FILE
#vault password file
#
-v, --verbose
#verbose mode (-vvv for more, -vvvv to enable connection debugging)
#执行详细输出
--version
#show program's version number and exit
#显示版本
Connection Options:
control as whom and how to connect to hosts
-k, --ask-pass
#ask for connection password
#
--private-key=PRIVATE_KEY_FILE, --key-file=PRIVATE_KEY_FILE
#use this file to authenticate the connection
#
-u REMOTE_USER, --user=REMOTE_USER
#connect as this user (default=None)
#指定远程主机以USERNAME运行命令
-c CONNECTION, --connection=CONNECTION
#connection type to use (default=smart)
#指定连接方式,可用选项paramiko (SSH)、ssh、local,local方式常用于crontab和kickstarts
-T TIMEOUT, --timeout=TIMEOUT
#override the connection timeout in seconds(default=10)
#SSH连接超时时间设定,默认10s
--ssh-common-args=SSH_COMMON_ARGS
#specify common arguments to pass to sftp/scp/ssh (e.g.ProxyCommand)
#
--sftp-extra-args=SFTP_EXTRA_ARGS
#specify extra arguments to pass to sftp only (e.g. -f, -l)
#
--scp-extra-args=SCP_EXTRA_ARGS
#specify extra arguments to pass to scp only (e.g. -l)
#
--ssh-extra-args=SSH_EXTRA_ARGS
#specify extra arguments to pass to ssh only (e.g. -R)
#
Privilege Escalation Options:
control how and which user you become as on target hosts
-s, --sudo
#run operations with sudo (nopasswd) (deprecated, use become)
#相当于Linux系统下的sudo命令
-U SUDO_USER, --sudo-user=SUDO_USER
#desired sudo user (default=root) (deprecated, use become)
#使用sudo,相当于Linux下的sudo命令
-S, --su
#run operations with su (deprecated, use become)
#
-R SU_USER, --su-user=SU_USER
#run operations with su as this user (default=root)(deprecated, use become)
-b, --become
#run operations with become (does not imply password prompting)
#
--become-method=BECOME_METHOD
#privilege escalation method to use (default=sudo),valid choices: [ sudo | su | pbrun | pfexec | doas |dzdo | ksu | runas ]
#
--become-user=BECOME_USER
#run operations as this user (default=root)
#
--ask-sudo-pass
#ask for sudo password (deprecated, use become)
#传递sudo密码到远程主机,来保证sudo命令的正常运行
--ask-su-pass
#ask for su password (deprecated, use become)
#
-K, --ask-become-pass
#ask for privilege escalation password
#
ansible,模板参数
https://blog.csdn.net/yuezhilangniao/article/details/112799990
https://blog.csdn.net/chitung_hsu/article/details/105470473
在 playbook 如何执行多个 shell,用 |
。
https://stackoverflow.com/questions/40230184/how-to-do-multiline-shell-script-in-ansible
tasks:
- name: run dmdb
shell: |
cd /ansible/distData/dockerBasicAppTar
docker load -i dm8-arm64-8.1.2.114-ent-2.tar
docker run -itd -p 5236:5236 --name dmdb -v /data/dmdbms:/opt/dmdbms/data -e DM_INIT_SYSDBA_PWD=Dameng8888 -e MAX_SESSIONS=1000 hub.dameng.com/arm64/cdb/dm8:8.1.2.114-ent-2
copy 模块在复制文件名为中文的文件的时候,会报错,会报错
An exception occurred during task execution. To see the full traceback, use -vvv. The error was: UnicodeDecodeError: 'ascii' codec can't decode byte 0xe4 in position 1: ordinal not in range(128)
fatal: [node_l1]: FAILED! => {"msg": "Unexpected failure during module execution.", "stdout": ""}
在网上一搜,说的多是说 shell 客户端的编码问题,实际上跟这无关
https://www.jianshu.com/p/5017d8342dd2
打成 jar 包,换成英文命名即可。
源目录以 /
结尾,会把源目录下所有的文件 copy 到目标地址。而不是把文件夹整个拷贝过去
playbook 脚本中的变量
ansible中使用playbook vars_files调用变量、vars_prompt交互式定义变量、魔法变量(特殊变量)及它的高级玩法_51CTO博客_ansible vars 中使用变量
变量调用方式:
通过{{ variable_name }} 调用变量,且变量名前后建议加空格,有时用“{{ variable_name }}”才生效
变量来源:
1.ansible 的 setup facts 远程主机的所有变量都可直接调用
2.通过命令行指定变量,优先级最高
ansible-playbook -e varname=value
3.在 playbook 文件中定义
vars:
- var1: value1
- var2: value2
4.在独立的变量 YAML 文件中定义
- hosts: all
vars_files:- vars.yml
5.在 /etc/ansible/hosts 中定义
- vars.yml
主机(普通)变量:主机组中主机单独定义,优先级高于公共变量
组(公共)变量:针对主机组中所有主机定义统一变量
6.在 role 中定义
使用 setup 模块中变量
在路径中使用变量,需要用 ""
把整个路径包起来
vars_files:
# 变量文件,这里可以放演示节点每个ip对应的节点id等等
- "{{ base_folder }}/ansible/vars/vars.yml"
在 playbook 脚本中,可以直接通过内置变量获取 hosts 文件中的信息,比如当前主机的 IP
比如我们的 hosts 文件为:
# 服务器组名不要使用 - 会警告
[dept_node]
node_l1 ansible_ssh_host=10.8.65.161 ansible_ssh_user="root" ansible_ssh_pass="dameng777" ansible_ssh_port=22
node_l2_1 ansible_ssh_host=10.8.65.189 ansible_ssh_user="root" ansible_ssh_pass="dameng777" ansible_ssh_port=22
node_l2_2 ansible_ssh_host=10.8.65.171 ansible_ssh_user="root" ansible_ssh_pass="dameng777" ansible_ssh_port=22
node_l3_1_1 ansible_ssh_host=10.8.65.190 ansible_ssh_user="root" ansible_ssh_pass="dameng777" ansible_ssh_port=22
node_l3_1_2 ansible_ssh_host=10.8.65.179 ansible_ssh_user="root" ansible_ssh_pass="dameng777" ansible_ssh_port=22
node_l3_2_1 ansible_ssh_host=10.8.65.193 ansible_ssh_user="root" ansible_ssh_pass="dameng777" ansible_ssh_port=22
# 所有三级节点都是数据生产节点
[data_generate_node]
node_l3_1_1 ansible_ssh_host=10.8.65.190 ansible_ssh_user="root" ansible_ssh_pass="dameng777" ansible_ssh_port=22
node_l3_1_2 ansible_ssh_host=10.8.65.179 ansible_ssh_user="root" ansible_ssh_pass="dameng777" ansible_ssh_port=22
node_l3_2_1 ansible_ssh_host=10.8.65.193 ansible_ssh_user="root" ansible_ssh_pass="dameng777" ansible_ssh_port=22
# [monitor_node]
# [audit_node]
test-ip.yml
这个 playbook:
- hosts: dept_node
remote_user: root
gather_facts: false
tasks:
- name: debug
debug: # 调试,可以用来打印参数之类的
# ansible_ssh_host 是个比较常用的变量,可以获取节点的ip
msg: "{{ansible_ssh_host}}"
输出:
PLAY [dept_node] ***************************************************************************************************************************
TASK [debug] *******************************************************************************************************************************
ok: [node_l1] => {
"msg": "10.8.65.161"
}
ok: [node_l2_2] => {
"msg": "10.8.65.171"
}
ok: [node_l2_1] => {
"msg": "10.8.65.189"
}
ok: [node_l3_1_1] => {
"msg": "10.8.65.190"
}
ok: [node_l3_1_2] => {
"msg": "10.8.65.179"
}
ok: [node_l3_2_1] => {
"msg": "10.8.65.193"
}
当然,使用 ansible_host
变量也能达到同样的效果,除次之外,我们还可以使用 ansible_ssh_port
、ansible_ssh_pass
、ansible_ssh_user
这些变量。非常方便
我们也可以在 hosts 文件中定义主机组的变量,比如 [dept_node:vars]
# 服务器组名不要使用 - 会警告
[dept_node]
node_l1 ansible_ssh_host=10.8.65.161 ansible_ssh_user="root" ansible_ssh_pass="dameng777" ansible_ssh_port=22
node_l2_1 ansible_ssh_host=10.8.65.189 ansible_ssh_user="root" ansible_ssh_pass="dameng777" ansible_ssh_port=22
node_l2_2 ansible_ssh_host=10.8.65.171 ansible_ssh_user="root" ansible_ssh_pass="dameng777" ansible_ssh_port=22
node_l3_1_1 ansible_ssh_host=10.8.65.190 ansible_ssh_user="root" ansible_ssh_pass="dameng777" ansible_ssh_port=22
node_l3_1_2 ansible_ssh_host=10.8.65.179 ansible_ssh_user="root" ansible_ssh_pass="dameng777" ansible_ssh_port=22
node_l3_2_1 ansible_ssh_host=10.8.65.193 ansible_ssh_user="root" ansible_ssh_pass="dameng777" ansible_ssh_port=22
# 所有三级节点都是数据生产节点
[data_generate_node]
node_l3_1_1 ansible_ssh_host=10.8.65.190 ansible_ssh_user="root" ansible_ssh_pass="dameng777" ansible_ssh_port=22
node_l3_1_2 ansible_ssh_host=10.8.65.179 ansible_ssh_user="root" ansible_ssh_pass="dameng777" ansible_ssh_port=22
node_l3_2_1 ansible_ssh_host=10.8.65.193 ansible_ssh_user="root" ansible_ssh_pass="dameng777" ansible_ssh_port=22
# [monitor_node]
# [audit_node]
[dept_node:vars]
test_var=123
然后,我们在 playbook 中就可以在指定 hosts 为这个组的时候,使用这个变量
- hosts: dept_node
remote_user: root
gather_facts: false
tasks:
- name: debug
debug: # 调试,可以用来打印参数之类的
msg: "{{test_var}}"
不过,我们最终还是建议,将所有的 playbook 变量都放到 vars.yaml
中进行统一的管理,hosts
文件就只放主机地址相关的信息
而且 playbook 中的 shell 模块是可以直接使用 vars_files
指定的 vars.yaml
的信息的(之前只在 template
中使用过,现在发现 shell
模块中也可以使用),而且除了 vars_files
还可以使用 vars_prompt
,在运行脚本的时候再提示用户输入,把用户的输入的内容来作为变量的值。
教程:ansible中使用playbook vars_files调用变量、vars_prompt交互式定义变量、魔法变量(特殊变量)及它的高级玩法_51CTO博客_ansible vars 中使用变量
参考博客:Ansible常见错误解析(停止更新)_non-zero return code_比永远更永远的博客-CSDN博客
有时候有这样一个重试场景,比如创建文件夹,mkdir folder,如果已经创建过了,再次执行的时候,是会报错的,这个时候,我们可以通过 mkdir folder > /dev/null 2>&1 将错误消除,这样,如果文件夹已存在,也不会报错,不存在,执行这个语句就创建了。
有时候这样还不够,因为有的时候,命令行必须返回点什么,不然就有可能是报错了,所以我们返回结果的行数, mkdir folder > /dev/null 2>&1 |wc -l ,这样会一直返回 0,
如果你不带 |wc -l
,命令执行没有输出,ansible 会认为命令行报错了,所以在 ansible 中必须带上。
ansible ,有时候会出现,命令行直接 ssh 可以通,但是通过 ansible 却不通,且提示,no route to the host 的错误,目前不知道怎么解决,TODO
使用ansible执行shell命令的正确姿势 | BingoStack
每一个 task 可以忽略错误 ignore_errors: true
tasks:
- name: run dmdb
ignore_errors: true
shell: |
cd /ansible/distData/dockerBasicAppTar
docker load -i dm8-arm64-8.1.2.114-ent-2.tar
docker run -itd -p 5236:5236 --name dmdb -v /data/dmdbms:/opt/dmdbms/data -e DM_INIT_SYSDBA_PWD=Dameng8888 -e MAX_SESSIONS=1000 hub.dameng.com/arm64/cdb/dm8:8.1.2.114-ent-2
模板文件
模板文件以 .j2
结尾,一般配合 playbook 中的 template
模块一起使用,
# 安装 dms
- hosts: dms_group
remote_user: root
gather_facts: false
vars_files:
# 变量文件,这里可以放演示节点每个ip对应的节点id等等
- /data/ansible/vars/vars.yml
tasks:
# 设置配置文件
- name: config dms properties file
# 执行主体是在管理机器
template:
src: /data/ansible/templates/application-prod.yml.j2
dest: /appData/dms/application-prod.yml
一般我们可以在模板文件中使用 playbook 中定义的变量,比如 vars_files
变量文件中的变量,假设
nodes:
node_1:
datasourceId: 1
node_2:
datasourceId: 2
node_3:
datasourceId: 3
names:
- aaa
- bbb
- ccc
使用变量方式跟直接在 playbook 中使用变量的方式一样,是直接 {{ nodes.node_1.datasourceId }}
即可,但是有的时候,我们需要在模板中遍历列表变量,比如 {{ nodes.names }}
,此时我们需要用到循环,使用方式如下:
{% for name in nodes.names %}
{{ name }}
{% endfor %}
有的的时候,一些内置变量会特别好用,比如在模板文件中获取指定主机组的所有的主机的 ip 地址:这在 nginx 反向代理目标服务器的时候,设置 nginx.conf
的时候非常有用
{% for host in groups['dms_group'] %}
server {{ hostvars[host]['ansible_ssh_host'] }}:8080;
{% endfor %}
可以用 for
循环,自然也就可以用 if-else
{% if nodes.is_cluster == 1 %}
type: redis
{% else %}
type: caffeine
{% endif %}
注意,当我们在模板中使用这样的语句的时候,{% %}
语句最好处在一行的最开头,否则 {% %}
语句包含的内容可能会存在缩进问题。在一些对缩进比较敏感的文件中可能会产生错误,比如 yml