Ansible

Ansible

Docker镜像

Github 地址:GitHub - ansible/ansible

官网:ansible

官方文档:Ansible Community

中文官方文档:Ansible中文权威指南 - 国内最专业的Ansible中文官方学习手册

参考博客:

Ansible 文件&拷贝模块 - 运维笔记

https://getansible.com/

一套比较完整的学习笔记,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中使用变量 - 运维派

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 文件中定义

主机(普通)变量:主机组中主机单独定义,优先级高于公共变量
组(公共)变量:针对主机组中所有主机定义统一变量

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_portansible_ssh_passansible_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

Web 页面

GitHub - ansible/awx: AWX provides a web-based user interface, REST API, and task engine built on top of Ansible. It is one of the upstream projects for Red Hat Ansible Automation Platform.