Ansible学习与实践

ansible简介(工作中能使用到的层次)

参考文档
ansible是新出现的自动化运维工具,ansible是一个配置管理和应用部署工具,基于Python开发,集合了众多运维工具(puppet、cfengine、chef、func、fabric.SaltStack )的优点,实现了批量系统配置、批量程序部署、批量运行命令等功能。
ansible是基于模块工作的,本身没有批量部署的能力。真正具有批量部署的是ansible所运行的模块,ansible只是提供一种框架,根据官方提供的信息,当前使用ansible的用户有:美国国家航空航天局NASA、evernote(印象笔记)、rackspace(全球三大云计算中心之一)、atlassian、twitter(全球互联网上访问量最大的十个网站之一)等

  1. ansible在生产环境当中的应用
    1. 自动化部署应用
    2. 自动化管理配置
    3. 自动化持续交付
    4. 自动化(aws)云服务器管理
  2. ansible的优点
    1. ansible糅合了众多老牌运维工具的优点,基本上pubbet和saltstack能实现的功能全部能实现
    2. 轻量级,无需在客户端安装agent,更新时,只需在操作机上进行一次更新即可
    3. ansible是一个工具,ansible不需要启动服务,仅仅只是一个工具,可以轻松的实现分布式扩展
    4. 批量任务执行可以写成脚本,而且不用分发到远程就可以执行
    5. ansible是一致性,高可靠性,安全性设计的轻量级自动化工具
    6. 使用python编写,维护更简单,ruby语法过于复杂
  3. 特性
    1. no agents:不需要在被管控主机上安装任何客户端
    2. no server:无服务器端,使用时直接运行命令即可
    3. modules in any languages:基于模块工作,可使用任意语言开发模块
    4. yaml,not code:使用yaml语言定制剧本playbook
    5. ssh by default:基于SSH工作
    6. strong multi-tier solution:可实现多级指挥(分布式)。

ansible架构以及执行流程

  1. 连接插件(connectior plugins): 用于连接主机(用来连接被管理端)
  2. 核心模块(core modules) :连接主机实现操作,它依赖于具体的模块来做具体的事情
  3. 自定义模块(custom modules): 根据自己的需求编写具体的模块
  4. 插件(plugins) :完成模块功能的补充
  5. 剧本(playbooks) :ansible的配置文件,将多个任务定义在剧本中,由ansible自动执行
  6. 主机清单(host inventory):定义ansible需要操作主机的范围
    最重要的一点是ansible是模块化的它所有的操作都依赖于模块
    基本架构
    比如需要创建一个文件,那么我就需要调用file模块;需要copy文件,那么我就需要copy模块;需要测试机器的存活率,那么就需要ping模块ansible all -m ping
    执行流程

ansible安装与配置

安装

  • ansible只是一个进程,不需要添加数据库也不需要启动和运行守护进程,它只是一个进程,你可以轻松使用它安装在任何一台主机上面(除了windows),ansible管理机不能安装到windows上面
  • 版本的选择,因为2.0有非常大的改进,一般都会使用2.0以上的版本
  • 控制机的要求,因为ansible是python写的,所以需要在安装了python2.6或者2.7以上的python版本才可以安装
  • 管理节点的要求,需要安装ssh,python版本在2.5以上
  • 安装有3个方式
    • yum源安装:yum -y install ansible
    • python模块安装:pip install ansible
    • 从github下载
    $ git clone git://github.com/ansible/ansible.git --recursive
    $ cd ./ansible
    $ make rpm
    $ sudo rpm -Uvh ./rpm-build/ansible-*.noarch.rpm

任务执行模式

ansible系统由控制主机对被管节点的操作方式有两种ad_hoc和playbook

  • ad_hoc单命令模式:可以对多台主机执行单个命令
ansible all -a "/bin/echo hello"
  • playbook通过多个tasks的集合完成一类功能如web的安装部署,数据库服务器的批量备份等
- hosts: webservers
  vars:
    http_port: 80
    max_clients: 200
  remote_user: root
  tasks:
  - name: ensure apache is at the latest version
    yum: name=httpd state=latest
  - name: write the apache config file
    template: src=/srv/httpd.j2 dest=/etc/httpd.conf
    notify:
    - restart apache
  - name: ensure apache is running (and enable it at boot)
    service: name=httpd state=started enabled=yes
  handlers:
    - name: restart apache
      service: name=httpd state=restarted

ansible(七个命令)

安装完ansible后,发现ansible一共为我们提供了七个指令:ansibleansible-docansible-galaxyansible-lintansible-playbookansible-pullansible-vault 。这里只查看usage部分,详细部分可以通过 “指令 -h” 的方式获取。

ansible

  • 格式ansible <host-pattern> [options]
  • 说明:ansible是指令核心部分,其主要用于执行ad-hoc命令,即单条命令。默认后面需要跟主机和选项部分,默认不指定模块时,使用的是command模块。
  • 举例ansible 127.0.0.1 -a 'date'
  • 常用参数
-m:执行模块的名字,默认使用command模块,所以如果是只执行单一命令可以不用-m参数 
-a:命令行参数
-u:ssh连接的用户名,默认用root,ansible.cfg中可以配置
-k:提示输入ssh登录密码。当使用密码验证的时候用
-s:sudo运行
-U:sudo到那个用户,默认为root
-K:提示输入sudo密码,当不是NOPASSWD模式时使用
-C:只是测试一下会改变什么内容,不会真正去执行
-c:连接类型(default=smart)
-f:fork多少个进程并发处理,默认为5个
-i:指定hosts文件路径(可以是文件,也可以是目录,也可以动态主机列表获取脚本),默认default=/etc/ansible/hosts
-I: 指定pattern,对<host_pattern>已匹配的主机中再过滤一次
--list-hosts:只打印有哪些主机会执行这个playbook文件,不是实际执行 #ansible --list-hosts all
-M:要执行的模块路径,默认为/usr/share/ansible
-o:压缩输出,摘要输出
--private-key:私钥路径
-T:ssh连接超时时间,默认10秒
-t:日志输出到该目录,日志文件名以主机名命名
-v:详细信息
-vvv: 查看详细信息,调试的时候使用
  • 主机清单文件:/etc/ansible/host
    主机清单文件

ansible-doc

  • 格式ansible-doc [options] [module...]
  • 说明:该指令用于查看模块信息,常用参数有两个-l 和 -s ,具体如下:
    • 列出所有已安装的模块ansible-doc -l
    • 查看具体某模块的用法,这里如查看command模块ansible-doc -s command

ansible-galaxy

  • 格式ansible-galaxy [-h] [--version] [-v] TYPE ...
  • 说明:用于方便的从<https://galaxy.ansible.com/ >站点下载第三方扩展模块,可以形象的理解其类似于centos下的yum、python下的pip或easy_install。
  • 举例:安装了一个aeriscloud.docker组件,前面aeriscloud是galaxy上创建该模块的用户名,后面对应的是其模块。在实际应用中也可以指定txt或yml文件进行多个组件的下载安装。这部分可以参看官方文档。ansible-galaxy install aeriscloud.docker

ansible-lint

对playbook的语法进行检查的一个工具。用法是ansible-lint playbook.yml

ansible-playbook

该指令是使用最多的指令,其通过读取playbook 文件后,执行相应的动作

ansible-pull

该指令使用需要谈到ansible的另一种模式---pull模式,这和平常经常用的push模式刚好相反,其适用于以下场景

  1. 你有数量巨大的机器需要配置,即使使用非常高的线程还是要花费很多时间;
  2. 你要在一个没有网络连接的机器上运行Anisble,比如在启动之后安装。

ansible-vault

ansible-vault主要应用于配置文件中含有敏感信息,又不希望他能被人看到,vault可以帮你加密/解密这个配置文件,属高级用法。主要对于playbooks里比如涉及到配置密码或其他变量时,可以通过该指令加密,这样我们通过cat看到的会是一个密码串类的文件,编辑的时候需要输入事先设定的密码才能打开。这种playbook文件在执行时,需要加上 –ask-vault-pass参数,同样需要输入密码后才能正常执行。

ansible-inventory

--graph:以结构化的方式显示出受管主机的信息

ansible(配置文件)

  • 位置:/etc/ansible/ansible.cfg
    用户可以修改一下配置文件来修改设置,他们的被读取的顺序如下:
* ANSIBLE_CONFIG (一个环境变量)
* ansible.cfg (位于当前目录中)
* .ansible.cfg (位于家目录中)
* /etc/ansible/ansible.cfg

Ansible 将会按以上顺序逐个查询这些文件,直到找到一个为止,并且使用第一个寻找到个配置文件的配置,这些配置将不会被叠加.

获取最新配置文件

如果使用程序包管理器安装ansible,最新的 ansible.cfg 配置文件有可能出现在 /etc/ansible 下并且命名为 ”.rpmnew”, 也可能根据不同的更新命名为其它名称。
如果你是通过 pip 或者其他方式安装,则可能需要自行创建这个文件,以免原配置文件被覆盖。

配置文件不同段详解

通用默认段

在 [defaults] 段中,以下选项是可以调节的:

action_plugins

“行为”是 ansible中的一段代码,用来激活一些事件,例如执行一个模块等。这是一个以开发者为中心的特性,使得一些底层模块可以从外部不同地方加载:

action_plugins = ~/.ansible/plugins/action_plugins/:/usr/share/ansible_plugins/action_plugins
ansible_managed

Ansible-managed 是一个字符串,可以插入到Ansible配置模版系统生成的文件中,如果你使用以下的自字符:{{ ansible_managed }}
例如:默认设置可以哪个用户修改和修改时间:

ansible_managed = Ansible managed: {file} modified on %Y-%m-%d %H:%M:%S by {uid} on {host}

这个设置可以告知用户,Ansible修改了一个文件,并且手动写入的内容可能已经被覆盖.需要注意的是,如果使用这一特性,这个字符串中将包含一个日期注释,如果日期更新,模版系统将会在每一次报告文件修改。

ask_pass*

控制Ansible剧本playbook是否会自动弹出输入密码,默认为no。如果使用SSH 密钥匙做身份认证,可能需要修改这一参数

ask_pass=True
ask_sudo_pass*

类似 ask_pass,用来控制Ansible playbook 在执行sudo之前是否询问sudo密码。默认为no。如果用户使用的系统平台开启了sudo 密码的话,应该修改这一参数

ask_sudo_pass=True
bin_ansible_callbacks

用来控制callback插件是否在运行 /usr/bin/ansible 的时候被加载。这个模块将用于命令行的日志系统,发出通知等特性。Callback插件如果存在将会永久性的被 /usr/bin/ansible-playbook 加载,不能被禁用:1.8 版本之前,callbacks 插件不可以被 /usr/bin/ansible加载。

bin_ansible_callbacks=False
callback_plugins

Callbacks 在ansible中是一段代码,在特殊事件时将被调用,并且允许出发通知。这是一个以开发者为中心的特性,可以实现对Ansible的底层拓展,并且拓展模块可以位于任何位置:

callback_plugins = ~/.ansible/plugins/callback_plugins/:/usr/share/ansible_plugins/callback_plugins
deprecation_warnings

允许在ansible-playbook输出结果中禁用“不建议使用”的警告:
“不建议警告”指的是使用一些在新版本中可能会被淘汰的遗留特性。

deprecation_warnings = True
display_skipped_hosts

如果设置为False,ansible将不会显示任何跳过任务的状态。默认选项是跳过任务的状态:
注意Ansible总是会显示任何任务的头文件, 不管这个任务被跳过与否。

display_skipped_hosts=True
error_on_undefined_vars

这个选项为默认,如果所引用的变量名称错误的话, 将会导致ansible在执行步骤上失败:

error_on_undefined_vars=True
# If set to False, any ‘{{ template_expression }}’ that contains undefined variables will be rendered in a template or ansible action line exactly as written.
executable

这个选项可以在sudo环境下产生一个shell交互接口。用户只在/bin/bash的或者sudo限制的一些场景中需要修改。大部分情况下不需要修改::

executable = /bin/bash
filter_plugins

过滤器是一种特殊的函数,用来拓展模版系统。这是一个开发者核心的特性,允许Ansible从任何地方载入底层拓展模块:

filter_plugins = ~/.ansible/plugins/filter_plugins/:/usr/share/ansible_plugins/filter_plugins
force_color

当没有使用TTY终端的时候,这个选项当用来强制颜色模式:

force_color = 1
force_handlers

即便这个用户崩溃,这个选项仍可以继续运行这个用户:

force_handlers = True
# The default is False, meaning that handlers will not run if a failure has occurred on a host. This can also be set per play or on the command line. See _handlers_and_failure for more details. 
# 如果这个选项是False. 如果一个主机崩溃了,handlers将不会再运行这个主机。这个选项也可以通过命令行临时使用。详见:doc:_handlers_and_failure.
forks*

这个选项设置在与主机通信时的默认并行进程数。很多用户把这个设置为50,有些设置为500或者更多,如果你有很多的主机, 高数值将会使得跨主机行为变快。默认值比较保守:

forks=5
hash_behaviour

Ansible默认将会以一种特定的优先级覆盖变量,详见:doc:playbooks_variables。拥有更高优先级的参数将会覆盖掉其他参数。
有些用户希望被hashed的参数(python 中的数据结构dictionaries)被合并.。这个设置叫做merge。这不是一个默认设置,而且不影响数组类型的数组。我不建议使用这个设置除非你觉得一定需要这个设置。官方实例中不使用这个选项
合法的值为replace(默认值)或者merge

hash_behaviour=replace
host_key_checking*

这个特性详见:doc:intro_getting_started,在Ansible 1.3或更新版本中将会检测主机密钥。如果你了解怎么使用并且希望禁用这个功能,你可以将这个值设置为False:

host_key_checking=True
inventory*

这个是默认库文件位置,脚本或者存放可通信主机的目录:

inventory = /etc/ansible/hosts
jinja2_extensions

这是一个开发者中心特性,允许开启Jinja2拓展模块:

jinja2_extensions = jinja2.ext.do,jinja2.ext.i18n
library*

这个是Ansible默认搜寻模块的位置:
Ansible会多个用冒号隔开的路径,同时也会搜索在playbook中的“./library”

library = /usr/share/ansible
log_path*

如果出现在ansible.cfg文件中,Ansible将会在选定的位置登陆执行信息。请留意用户运行的Ansible对于logfile有无权限:
这个特性不是默认开启的。如果不设置,ansible将会把模块加载记录在系统日志系统中。不包含用户密码.
对于需要了解更多日志系统的企业及用户,你也许对**:doc:tower**感兴趣。

log_path=/var/log/ansible.log
lookup_plugins

这是一个开发者中心选项,允许模块插件在不同区域被加载:

lookup_plugins = ~/.ansible/plugins/lookup_plugins/:/usr/share/ansible_plugins/lookup_plugins
module_lang

这是默认模块和系统之间通信的计算机语言,默认为C语言

module_lang = C
# 在解决推送日志乱码的时候需要配置
module_lang = C 
    ==> module_lang = zh_CN.UTF-8
module_set_locale = False  
    ==> module_set_locale = True
module_name

这个是/usr/bin/ansible的默认模块名(-m)。默认是command模块。之前提到过,command模块不支持shell变量、管道、配额,所以也许你希望把这个参数改为shell

module_name = command
nocolor

默认ansible会为输出结果加上颜色,用来更好的区分状态信息和失败信息。如果你想关闭这一功能,可以把nocolor设置为1:

nocolor=0
nocows

默认ansible可以调用一些cowsay的特性,使得/usr/bin/ansible-playbook运行起来更加愉快。如果你不喜欢cows,你可以通通过将nocows设置为1来禁用这一选项:

nocows=0
pattern

如果没有提供“hosts”节点,这是playbook要通信的默认主机组。默认值是对所有主机通信:
注意/usr/bin/ansible 一直需要一个host pattern,并且不使用这个选项。这个选项只作用于/usr/bin/ansible-playbook。

pattern = * 
# hosts=*
poll_interval*

对于Ansible中的异步任务,当具体的poll interval 没有定义时,多少时间回查一下这些任务的状态,默认值(15秒)是一个折中选择。这个时间是回查频率、任务完成叫回频率、当任务完成时的回转频率的折中:

poll_interval=15
private_key_file

如果你是用pem密钥文件而不是SSH客户端或密码认证的话,你可以设置这里的默认值,来避免每一次提醒设置密钥文件位置–ansible-private-keyfile:

private_key_file=/path/to/file.pem
remote_port*

这个设置是你系统默认的远程SSH端口,如果不指定,默认为22号端口:

remote_port = 22
remote_tmp*

Ansible 通过远程传输模块到远程主机,然后远程执行,执行后再清理现场。在有些场景下,你也许想使用默认路径,希望像更换补丁一样使用, 这时候你可以使用这个选项:
默认路径是在用户家目录下的目录.Ansible,会在这个目录中使用一个随机的文件夹名称

remote_tmp = $HOME/.ansible/tmp
remote_user

这是个ansible使用/usr/bin/ansible-playbook链接的默认用户名。如果不指定,/usr/bin/ansible默认使用当前用户名称

remote_user = root
roles_path

roles路径指的是roles/下的额外目录,用于playbook搜索Ansible roles。比如, 如果我们有个用于common roles源代码控制仓库和一个不同的playbooks仓库,你也许会在 /opt/mysite/roles里面查找roles:

roles_path = /opt/mysite/roles

多余的路径可以用冒号分隔,类似于其他path字符串:

roles_path = /opt/mysite/roles:/opt/othersite/roles
sudo_exe

如果在其他远程主机上使用另一种方式执行sudo操纵, sudo程序的路径可以用这个参数更换,使用命令行标签来拟合标准sudo:

sudo_exe=sudo
sudo_flags

当使用sudo支持的时候,传递给sudo以外的标签. 默认值为-H, 意思是保留原用户的环境。在有些场景下也许需要添加或者删除标签,大多数用户不需要修改这个选项:

sudo_flags=-H
sudo_user*

这个是sudo使用的默认用户,如果–sudo-user没有特指或者sudo_user在Ansible playbooks中没有特指,在大多数的逻辑中 默认为root

sudo_user=root
system_warnings

允许禁用系统运行ansible相关的潜在问题警告(不包括操作主机):

system_warnings = True
timeout*

这个是默认SSH链接尝试超市时间:

timeout = 10
transport*

如果-c <transport_name>选项没有在使用/usr/bin/ansible或者 /usr/bin/ansible-playbook特指的话,这个参数提供了默认通信机制。默认 值为smart, 如果本地系统支持 ControlPersist技术的话,将会使用(基于OpenSSH)ssh,如果不支持讲使用paramiko。其他传输选项包括localchrootjail等等。
用户通常可以设置为smart,让playbook在根据通信条件自己选择connectin:参数.

transport=smart
vars_plugins

这是一个开发者中心选项,允许底层拓展模块从任何地方加载:

vars_plugins = ~/.ansible/plugins/vars_plugins/:/usr/share/ansible_plugins/vars_plugins
vault_password_file

这个用来设置密码文件,也可以通过命令行指定–vault-password-file

vault_password_file = /path/to/vault_password_file

Paramiko Specific Settings

Paramiko是商业版linux 6 的默认SSH链接。但在其他平台上不是默认使用的。请在[paramiko]头文件下激活它

record_host_keys

默认设置会记录并验证通过在用户hostfile中新发现的的主机(如果host key checking 被激活的话)。这个选项在有很多主机的时候将会性能很差。在这种情况下,建议使用SSH传输代替。当设置为False时, 性能将会提升,在hostkey checking被禁用时候,建议使用:

record_host_keys=True

OpenSSH Specific Settings

[ssh_connection]头文件之下,用来调整SSH的通信连接。OpenSSH是Ansible在操作系统上默认的通讯连接。

ssh_args

如果设置了的话,这个选项将会传递一组选项给Ansible,而使用以前的默认值:

ssh_args = -o ControlMaster=auto -o ControlPersist=60s
# 用户可以提高ControlPersist值来提高性能。30分钟通常比较合适.
control_path

这个是保存ControlPath套接字的位置。默认值是:

control_path=%(directory)s/ansible-ssh-%%h-%%p-%%r

在有些系统上面,会遇到很长的主机名或者很长的路径名称(也许因为很长的用户名,或者比较深的家目录),这些都会超出套接字文件名字符上限(对于大多数平台上限为108个字符)。在这种情况下,你也许希望按照以下方式缩短字符串:

control_path = %(directory)s/%%h-%%r
scp_if_ssh

用户操控一个没有开启SFTP协议的远程系统,如果这个设置为True,scp将代替用来为远程主机传输文件:

scp_if_ssh=False

如果没有遇到这样的问题没有必要来修改这个设置。当然修改这个设置也没有什么明显的弊端。大部分的系统环境都默认支持SFTP, 通常情况下不需要修改。

pipelining

在不通过实际文件传输的情况下执行ansible模块来使用管道特性,从而减少执行远程模块SSH操作次数。如果开启这个设置,将显著提高性能.。然而当使用sudo操作的时候,,你必须在所有管理的主机的/etc/sudoers中禁用requiretty。默认这个选项为了保证与sudoers requiretty的设置的兼容性是禁用的.,但是为了提高性能强烈建议开启这个设置。

pipelining=False

Accelerated Mode Settings

[accelerate]首部下, 以下设置可以调整。如果你不能在你的环境中开启:ref:pipelining , Accelertation 是一个很有用的性能特性. 但是如果你可以开启管道,这个选项也许对你无用。

accelerate_port

在急速模式下使用的端口:

accelerate_port = 5099
accelerate_timeout

这个设置时用来控制从客户机获取数据的超时时间。如果在这段时间内没有数据传输,套接字连接会被关闭。一个保持连接(keepalive)数据包通常每15秒回发回给控制台,所以这个超时时间不应该低于15秒(默认值为30秒):

accelerate_timeout = 30
accelerate_connect_timeout

这个设置连接套接字调用的超时时间。这个应该设置相对比较短.这个和accelerate_port连接在回滚到ssh或者paramiko(受限于你默认的连接设置)连接方式之前会尝试三次开始远程加速daemon守护进程。默认设置为1.0秒:

accelerate_connect_timeout = 1.0

注意,这个选项值可以设置为小于1秒钟,但是除非你拥有一个速度很快而且很可靠的网络,否则也许这样并不是一个很好的选择。如果你使用英特网访问你的系统,最好提高这个值。

accelerate_daemon_timeout

这个控制加速daemon守护进程的超时时间,用分钟来衡量。默认为30分钟:

accelerate_daemon_timeout = 30
accelerate_multi_key

如果这个选项开启,这个设置将允许多个私钥被加载到daemon。任何客户端要想连接daemon都需要开启这个选项:
通过本地套接字文件连接的通过SSH上传密钥文件到目标节点的新客户端,必须在登陆daemon时使用原始的登陆密钥登陆。

accelerate_multi_key = yes

ansible(免密码登录以及排错)

  1. 生成密钥
    ssh-keygen -t rsa

【常用ssh-keygen参数】
-b bits :指定密钥长度。对于RSA密钥,最小要求768位,默认是2048位。DSA密钥必须恰好是1024位(FIPS 186-2 标准的要求)
-t type :指定要创建的密钥类型。可以使用:”rsa1”(SSH-1) “rsa”(SSH-2) “dsa”(SSH-2)
-f filename :指定密钥文件名
-C comment :提供一个新注释
-q :安静模式。用于在 /etc/rc 中创建新密钥的时候

  1. 将密钥拷贝到预管理的节点(root@192.168.1.203)上
    ssh-copy-id -i /root/.ssh/id_rsa.pub root@192.168.1.203
  2. 测试
    ansible all -a "date"
    ansible all -a "ping -c 4 192.168.3.1" -vvv

ansible插件ansible-cmdb(实现cmdb功能)

  1. 安装 ansible-cmdb 插件
wget https://github.com/fboender/ansible-cmdb/releases/download/1.30/ansible-cmdb-1.30.tar.gz
tar -xvf ansible-cmdb-1.30.tar.gz -C /usr/share/ansible/plugins
cd /usr/share/ansible/plugins/ansible-cmdb-1.30/ && make install
  1. 添加环境变量
vim /etc/profile
source /etc/profile
  1. 生成所有主机的facts信息
ansible all -m setup --tree out/
# 会在当前目录下生成 out 目录,out目录下都是以主机域名或ip命名的文件。
# 比如,在/usr/local/ 下执行 ansible all -m setup --tree out/ ,则会在 /usr/local/下生成out目录
# 或者,
# 直接指明在哪生成 out目录
ansible all -m setup --tree /usr/local/out
  1. 通过第3步生成的facts信息生成web页面
cd ~
ansible-cmdb /usr/local/out/ > overview.html
将 ~/overview.html 文件导出可用浏览器直接访问:

ansible组件

inventory(主机清单静态)

所有的机器信息都存放到ansible的inventory组件里面,默认ansible的inventory是一个静态的ini格式的文件/etc/ansible/hosts,当然还可以通过ANSIBLE_HOSTS环境变量指定或者运行ansible和ansible-playbook的时候用-i参数临时设置。
inventory

中括号中的名字代表组名,你可以根据你自己的需求将庞大的主机分成具有标识的组,如上面我分了两个组webservers和dbservers组;
主机(hosts)部分可以使用域名、主机名、IP地址表示;当然使用前两者时,也需要主机能反解析到相应的IP地址,一般此类配置中多使用IP地址。

定义主机和主机组

[docker] #定义了一组叫docker
172.16.1.11 #组下面的主机
172.11.11.11 # ansible_ssh_pass='123456'

[docker:vars] #针对docker组使用inventroy内置变量定义了ssh登陆密码
ansible_ssh_pass='123456'
aaa='my name is aaa' # 传入自定义变量

[ansible:children] #定义了ansible组下面包含docker组,即docker组为ansible的子组
docker

主机清单文件在修改后会立即生效,一般使用ansible-inventory --graph命令以结构化的方式显示出受管主机的信息。因为我们对受管主机进行了分组,因此这种方式非常便于我们的阅读。

[root@linuxprobe ~]# ansible-inventory --graph
@all:
  |--@balancers:
  |  |--192.168.10.24
  |--@dev:
  |  |--192.168.10.20
  |--@prod:
  |  |--192.168.10.22
  |  |--192.168.10.23
  |--@test:
  |  |--192.168.10.21
  |--@ungrouped:

多个inventory列表

配置支持多个inventory,首先需要修改ansible.cfg中hosts的定义改成一个目录比如hostfile = /data/inventory,然后我们在目录里面放入多个hosts文件

[root@ceshi2 data]# tree inventory
inventory
├── docker
└── hosts

不同的文件可以存放不同的主机:

[root@ceshi2 data] cat hosts
172.16.4.11 ansible_ssh_pass='123456'

[root@ceshi2 data]cat docker
[docker]
172.16.1.11 #组下面的主机
172.11.11.11 # ansible_ssh_pass='123456'

[docker:vars] #针对docker组使用inventroy内置变量定义了ssh登陆密码
ansible_ssh_pass='123456'

[ansible:children]#定义了一个ansible组 下面包含一个docker组
docker

inventory内置参数

  • ansible_ssh_host:要连接的主机名
  • ansible_ssh_port:端口号默认是22
  • ansible_ssh_user:ssh连接时默认使用的用户名
  • ansible_ssh_pass:ssh连接时的密码
  • ansible_sudo_pass:使用sudo连接用户是的密码
  • ansible_ssh_private_key_file:秘钥文件如果不想使用ssh-agent管理时可以使用此选项
  • ansible_shell_type:shell的类型默认sh
  • ansible_connection:SSH 连接的类型: local , ssh , paramiko。在ansible 1.2之前默认是paramiko,后来智能选择,优先使用基于ControlPersist的ssh
  • ansible_python _ interpreter:用来指定 python 解释器的路径,同样可以指定ruby 、perl 的路径

inventory(动态获取主机清单)

动态inventory的意思是所有的变量可以从外部获取,也就是说我们可以从CMDB以及zabbix系统拉取所有的主机信息然后使用ansible进行管理。引用inventory只需要把ansible.cfg文件中的inventory定义值改成一个执行脚本即可。
脚本举例:

#!/usr/bin/env python
# coding=utf-8
# inverti.py
import json
host1ip = ['192.168.1.15']
host2ip = ['192.168.1.110']
group = 'test11'
group2 = 'test22'
hostdata = {group:{"hosts":host1ip},group2:{"hosts":host2ip}} # ansible识别的是json格式,且格式固定为{"组名":{"hosts":ip1}}
print json.dumps(hostdata,indent=4)

执行:

[root@vagrant-centos65 opt]# ansible -i inverti.py all  -a 'uptime' -k
SSH password: 
192.168.1.15 | SUCCESS | rc=0 >>
 07:25:27 up  3:56,  3 users,  load average: 0.00, 0.00, 0.00
192.168.1.110 | SUCCESS | rc=0 >>
 07:25:27 up 7 min,  3 users,  load average: 0.00, 0.02, 0.00

ansible(单命令模式)

ad-hoc是点对点的执行ansible命令,介绍一下日常的Ad-Hoc命令
例如:

# 后台执行(返回ansible_job_id(即jid)等信息)
# -B的意思是异步执行,-o的意思是压缩输出,-P设置轮询间隔
ansible docker -B 120 -P 0 -m shell -a 'sleep 10;hostname' -o #加了-P 0 之后会返回一个job_id 可以通过jobID去查看执行的结果。当-P 大于0的时候会轮询去查询执行结果
# 根据jid查询后台任务状态
# -m要执行的模块,-a模块参数
ansible all -m async_status -a 'jid='5265654654'' 

其他的命令

可选参数:
  --ask-vault-pass      
	要求提供保管库密码
  --list-hosts          
	输出匹配主机列表;不执行其他任何操作
  --playbook-dir BASEDIR
	由于此工具不使用剧本,因此可以将其用作替代剧本目录,从而为许多功能(包括role / group_vars /等)设置相对路径。
  --syntax-check
	在剧本上执行语法检查,但不执行
  --vault-id VAULT_IDS
	要使用的库身份
  --vault-password-file VAULT_PASSWORD_FILES
	保险库密码文件
  --version             
	显示程序的版本号,配置文件位置,配置的模块搜索路径,模块位置,可执行位置和退出
  -B SECONDS, --background SECONDS
	异步运行,在X秒后失败。(默认值= N / A)
  -C, --check           
	不要做任何改变;相反,尝试预测可能发生的某些变化
  -D, --diff            
	更改(小的)文件和模板时,请显示这些文件中的差异;与--check一起使用效果很好
  -M MODULE_PATH, --module-path MODULE_PATH
	在模块库前添加用冒号分隔的路径。(默认值=〜/ .ansible / plugins / modules:/ usr / share / ansible / plugins / modules)
  -P POLL_INTERVAL, --poll POLL_INTERVAL
	如果使用-B,则设置轮询间隔(默认值= 15)
  -a MODULE_ARGS, --args MODULE_ARGS
	模块参数
  -e EXTRA_VARS, --extra-vars EXTRA_VARS
	如果文件名以@开头,则将其他变量设置为key = value或YAML / JSON
  -f FORKS, --forks FORKS
	指定要使用的并行进程数。(默认值= 5)
  -h, --help            
	显示此帮助消息并退出
  -i INVENTORY, --inventory INVENTORY, --inventory-file INVENTORY
	指定清单主机路径或逗号分隔的主机列表。 --inventory-file已弃用。
  -l SUBSET, --limit SUBSET
	将所选主机进一步限制为其他模式
  -m MODULE_NAME, --module-name MODULE_NAME
	要执行的模块名称。(默认=commond)
  -o, --one-line        
	压缩输出
  -t TREE, --tree TREE  
	日志输出到该目录
  -v, --verbose         
	详细模式(-vvv用于更多,-vvvv用于启用连接调试)
--------------------------------------------------------------
--------------------------------------------------------------
特权升级选项:控制您如何以及在目标主机上成为哪个用户
  --become-method BECOME_METHOD
    要使用的特权升级方法(默认= sudo),请使用“ ansible-doc -t become -l”列出有效的选择。
  --become-user BECOME_USER
	以该用户身份运行操作(默认= root)
  -K, --ask-become-pass
	要求特权升级密码
  -b, --become          
	使用变为运行操作(不表示提示输入密码)
--------------------------------------------------------------
--------------------------------------------------------------
连接选项:控制谁以及如何连接到主机
  --private-key PRIVATE_KEY_FILE, --key-file PRIVATE_KEY_FILE
	使用此文件来验证连接
  --scp-extra-args SCP_EXTRA_ARGS
	指定额外的参数以仅传递给scp(例如-l)
  --sftp-extra-args SFTP_EXTRA_ARGS
	指定额外的参数以仅传递给sftp(例如-f,-l)
  --ssh-common-args SSH_COMMON_ARGS
	指定要传递给sftp / scp / ssh的通用参数(例如ProxyCommand)
  --ssh-extra-args SSH_EXTRA_ARGS
	指定额外的参数以仅传递给ssh(例如-R)
  -T TIMEOUT, --timeout TIMEOUT
	覆盖连接超时(以秒为单位)。(默认值为10)
  -c CONNECTION, --connection CONNECTION
	要使用的连接类型。(默认=智能)
  -k, --ask-pass        
	询问连接密码
  -u REMOTE_USER, --user REMOTE_USER
	以该用户身份连接。(默认=无)

ansible常用模块

根据官方的分类,将模块按功能分类为:云模块、命令模块、数据库模块、文件模块、资产模块、消息模块、监控模块、网络模块、通知模块、包管理模块、源码控制模块、系统模块、单元模块、web设施模块、window模块等

command

命令模块,适合使用简单的命令 无法支持"<",">","|",";","&"等符号

  • chdir:在执行命令前,进入到指定目录中
  • creates:判断指定文件是否存在,如果存在,不执行后面的操作
  • removes:判断指定文件是否存在,如果存在,执行后面的操作
  • free_form:必须要输入一个合理的命令
ansible webservers -m command -a "hostname"
ansible webservers -m command -a "chdir=/data ls -l"
ansible webservers -m command -a "touch /data/lol.txt creates=/data/lol.txt"
ansible webservers -m command -a "rm -f /data/hosts removes=/data/hosts"

shell

类似command模块升级版—万能模块,可以使用"<",">","|",";","&"等符号特殊符号,其它参数参考command模块,使用方法一致

  • chdir:在执行命令前,进入到指定目录中
  • creates:判断指定文件是否存在,如果存在,不执行后面的操作
  • removes:判断指定文件是否存在,如果存在,执行后面的操作
  • free_form:必须要输入一个合理的命令
ansible dkaiyun -m shell -a "ps -ef |grep /[s]sh"

ping

主要用于判断远程客户端是否在线,用于ping本身服务器,用法很简单,不涉及参数,返回值是changed、ping

ansible all -m ping
ping

setup

主要用于获取主机信息,不用option的情况会输出所有相关的对象机器的facts,在playbooks里经常会用到的一个参数gather_facts就与该模块有关。还有一个经常使用的参数是filter,使用方法如下:

 # 查看所有的信息:CPU、内存、IP等,所有的主机信息都会被收集
ansible all -m setup

# filter :过滤关键字
# 查看主机的内存信息
ansible all -m setup -a 'filter="ansible_*_mb"' 
# 查看主机eth0-eth2的网卡信息
ansible all -m setup -a 'filter=ansible_eth[0-2]'
# 收集对象机器的环境变量信息
ansible all -m setup -a "filter=ansible_env"

# --tree :将所有主机的输出信息保存到/tmp/目录下,以/etc/ansible/hosts里的主机名为文件名
ansible all -m setup -a 'filter=ansible_distribution_version' --tree /tmp/
# 把所有的主机信息输入到/tmp/inode目录下,每台主机的信息输入到主机文件名的文件中
ansible all -m setup --tree /tmp/inode

# gather_subset:按子集收集信息,值有all, min, hardware, network, virtual, ohai, facter。不包含请使用!号,如,!network

# 其他常用参数如下:
# 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个数(只显示总的个数)
setup

file

file模块主要用于远程主机上的文件操作,file模块包含如下选项:

  • force:需要在两种情况下强制创建软连接,一是源文件不存在但之后会建立的情况下,二是目标软连接已存在,需要先取消之前的软连接,然后再创建软连接,两个选项:yes|no
  • group/mode/owner:定义文件或目录的属组/权限/属主
  • path:必选项,定义文件/目录的路径
  • recurse:递归的设置文件的属性,只对目录有效
  • scr:要被链接的源文件的路径,只应用于state=link的情况
  • dest:被链接到的路径,只应用于state=link的情况
  • state=
    • directory:如果目录不存在,创建目录
    • link:创建软连接
    • hard:创建硬链接
    • touch:如果文件不存在,则创建文件;如果文件或目录已经存在,则更新其最后修改时间
    • absent:删除目录、文件、链接文件
    • file:查看指定目录信息是否存在
ansible all -m file -a "src=/etc/fstab dest=/tmp/fstab state=link"
ansible all -m file -a "path=/tmp/fstab state=absent"
ansible all -m file -a "path=/tmp/fstab state=touch"
file

copy

复制文件到远程主机,copy模块包含如下选项:

  • backup:在覆盖之前将原文件备份,备份文件包含时间信息。有两个选项:yes|no。只有当两个文件的内容不同时才生效
  • content:用于替代src,可以直接设定指定文件的值
  • dest:必选项。要将源文件复制到远程主机的绝对路径,如果源文件是一个目录,那么该路径必须是一个目录
  • directory_mode:递归的设定目录的权限,默认为系统默认权限
  • force:如果目标主机包含该文件,但内容不同,如果设置为yes(默认值),则强制覆盖;如果为no,则只有当目标主机的目标位置不存在该文件时才复制
  • others:所有的file模块里的选项都可以在这里使用
  • src:要复制到远程主机的文件在本地的地址,可以是绝对路径也可以是相对路径。如果路径是一个目录,它将递归复制。在这种情况下,如果路径使用"/"结尾,则只复制目录中的内容,否则,包含整个目录在内的全部内容均被复制,类似rsync
  • validate:验证命令在复制到位之前运行。 要验证的文件的路径通过“%s”传递,该路径必须如下面的visudo示例中所示。
ansible all -m copy -a "src=/etc/fstab dest=/data owner=foo group=foo mode=0644" # mode权限之前需要添加一位0。如果想要在user对应的权限位上添加执行权限,则可以使用mode=u+x表示
ansible all -m copy -a "src=/root/anaconda-ks.cfg  dest=/data"
echo ansible >>/root/anaconda-ks.cfg
ansible all -m copy -a "src=/root/anaconda-ks.cfg  dest=/data backup=yes"
ansible all -m copy -a "src=/etc/sudoers dest=/data validate='visudo -cf %s'" # -cf的意思是检查配置文件是否正常

copy
backup
backup2

fetch

抓取文件到管理机上

  • src:要获取的远程系统上的文件,必须是文件,而不是目录
  • dest:用于保存文件的目录
ansible all -m fetch -a "src=/root/lol.txt dest=/root"

service

用于管理服务,该模块包含如下选项:

  • arguments:给命令行提供一些选项
  • enabled:是否开机启动yes|no
  • name:必选项,服务名称
  • pattern:定义一个模式,如果通过status指令来查看服务的状态时,没有响应,就会通过ps指令在进程中根据该模式进行查找,如果匹配到,则认为该服务依然在运行
  • runlevel:运行级别
  • sleep:如果执行了restarted,则在stop和start之间沉睡几秒钟
  • status=:对当前服务执行
    • started:启动
    • stopped:停止
    • restarted:重启
    • reloaded:重新加载
ansible all -m service -a "name=crond state=started enabled=yes"
ansible all -m service -a "name=nginx state=restarted sleep=10"
ansible all -m service -a "name=foo pattern=/usr/bin/foo state=started"
ansible all -m service -a "name=network state=restarted args=ens32" # centos6

cron

用于管理计划任务,包含如下选项:

  • backup:对远程主机上的原任务计划内容修改之前做备份
  • cron_file:如果指定该选项,则用该文件替换远程主机上的cron.d目录下的用户的任务计划
  • day/hour/minute/month/weekday:日/小时/分钟/月/周
  • job:要这行的任务,依赖于state=present
  • name:该任务的描述
  • special_time:指定什么时候执行,参数reboot/yearly/annually/monthly/weekly/daily/hourly
  • state:确认该任务计划是创建还是删除
  • user:以哪个用户的身份执行
  • diasbled=yes:将指定的任务注释掉,取消注释使用no。注释和取消注释时必须填写 job和时间参数
# 添加任务计划
ansible all -m cron -a 'name="a job for reboot" special_time=reboot job="/some/job.sh"'
ansible all -m cron -a "name='testx' weekday='2' minute='0' hour=12 user='root' job='cat /etc/passwd >/root/111' cron_file='test ansible'"
ansible all -m cron -a 'backup="True" name="test" minute=0 hour=2 job="ls -alh >/dev/null"'
ansible all -m cron -a "name='ntpdate time' minute=*/5 job='/usr/sbin/ntpdate ntp1.aliyun.com &>/dev/null' "
# 删除任务计划
ansible all -m cron -a "name='a job for reboot' state=absent" 
ansible all -m cron -a "name='test' state=absent" 
ansible all -m cron -a "name='testx' state=absent" 
ansible all -m cron -a 'cron_file=ansible_yum-autoupdate state=absent'
# 注释和取消注释任务
ansible all -m cron -a "name='ntpdate time' minute=*/5 job='/usr/sbin/ntpdate ntp1.aliyun.com &>/dev/null' disabled=yes"
ansible all -m cron -a "name='ntpdate time' minute=*/5 job='/usr/sbin/ntpdate ntp1.aliyun.com &>/dev/null' disabled=no"

1
2
3
result

yum

使用yum包管理器来管理软件包,其选项有:

  • config_file:yum的配置文件
  • diasble_gpg_check:关闭gpg_check校验
  • disablerepo/enablerepo:启用/关闭某个源
  • name:要进行操作的软件包的名字,也可以传递一个url或者笨的的rpm包的路径
  • state=
    • absent:卸载软件(常用)
    • removed:卸载软件
    • present:安装软件(常用)
    • installed:安装软件
    • latest:安装最新版本软件,更新软件
ansible all -m yum -a "name=httpd state=latest"
ansible all -m yum -a "name='@Development tools' state=present"
ansible all-m yum -a 'name=nginx state=installed disable_gpg_check=yes'

get_url

该模块主要用于从http\ftp\https服务器上下载文件,类似于wget。

  • sha256sum:下载完成后进行sha 256校验
  • timeout:下载超时时间,默认10秒
  • url:下载的url
  • url_password\url_username:主要用于需要用户名和密码进行验证的情况
  • use_proxy:使用代理,代理需要先在环境变量中定义
ansible all -m get_url -a "url=https://www.baidu.com dest=/tmp/baidu.html"

mount

用于批量管理主机进行挂载卸载操作

  • fstype:指定挂载的文件系统类型
  • opts:指定挂载的参数信息
  • path:定义一个挂载点信息
  • src:定义设备文件信息
  • state=
    • absent:会进行卸载,也会修改fstab文件信息
    • unmounted:会进行卸载,不会修改fstab文件
    • present:不会挂载,只会修改fstab文件
    • mounted:会进行挂载,会修改fstab文件
# 只是在/etc/fstab文件中添加了配置信息,不会真正进行挂载(mount -a)
ansible all -m mount -a "src=172.16.1.31:/data/  path=/mnt fstype=nfs state=present"
# 在/etc/fstab文件中添加了配置信息,并且也会真正进行挂载
ansible web01 -m mount -a "src=172.16.1.31:/data/  path=/mnt fstype=nfs state=mounted"
# 在进行挂载的时候,使用state=mounted
# 在进行卸载的时候,使用state=absent

group

远程批量创建用户组信息。

  • gid:指创建的组ID信息
  • name:指创建组名称信息
  • state=
    • absent:删除指定的用户组
    • present:创建指定的用户组
ansible all -m group -a "name=test gid=1055"
ansible all -m group -a "name=test gid=1055 state=absent"

user

远程批量创建用户信息。

  • password:请输入密码信息。password设置密码时不能使用明文方式,只能使用密文方式,可以给用户设置密码,还可以给用户修改密码
  • name:指定用户名信息
  • uid:指定用户uid信息
  • group:指定用户主要属于哪个组
  • groups:指定用户属于哪个附加组信息
  • shell /bin/bash或/sbin/nologin:指定是否能够登录
  • create_home:yes/no,是否创建家目录信息
  • home:指定家目录创建在什么路径 默认/home

template

可以将带有参数的配置文件传递到目标地址,可以对文件进行属组属主的修改以及备份(类似copy模块)。

  • backup:建立个包括timestamp在内的文件备份,以备不时之需.
  • dest:远程节点上的绝对路径,用于放置template文件。
  • group:设置远程节点上的的template文件的所属用户组
  • mode:设置远程节点上的template文件权限。类似Linux中chmod的用法
  • owner:设置远程节点上的template文件所属用户
  • src:本地Jinjia2模版的template文件位置。
# 在playbook中的使用方法
# 把/mytemplates/foo.j2文件经过填写参数后,复制到远程节点的/etc/file.conf,文件权限相关略过
- template: src=/mytemplates/foo.j2 dest=/etc/file.conf owner=bin group=wheel mode=0644
# 跟上面一样的效果,不一样的文件权限设置方式
- template: src=/mytemplates/foo.j2 dest=/etc/file.conf owner=bin group=wheel mode="u=rw,g=r,o=r"

ansible-playbook

playbook基本语法

playbook使用yaml语法,yaml语法可以通过https://yaml.org/spec/1.2/spec.htmlhttps://www.runoob.com/w3cnote/yaml-intro.html进行详细的学习。
下面是简单的例子:

例子1:简单说明playbook的书写方式

  1. playbook文件-nginx.yml
---
- hosts: all
  tasks:
  - name: Install nginx
    yum: name=nginx state=present
  - name: template nginx.conf
    template: src=./nginxbak.conf dest=/etc/nginx/nginx.conf owner=root group=root mode=0644 validate='nginx -t -c %s'
    notify:
      - Restart Nginx Service
  handlers:
  - name: Restart Nginx Service # name的值必须与notify的值相同,否则无法触发handle
    service: name=nginx state=restarted
  • 第1行:表示该文件是YAML文件,非必须
  • 第2行:定义了该playbook针对的目标主机,all表示针对所有主机,这个参数支持ansible Ad-Hoc模式的所有参数,也就是可以定义组
  • 第3行:定义了该playbook所有的tasks集合,比如下面的3个tasks
  • 第4行:定义了一个task的名称,非必须,建议根据task实际任务命名
  • 第5行:定义了一个状态的action,这里使用的是yum模块安装nginx软件包
  • 第6-9行:使用template模板去管理/etc/nginx/nginx.conf文件;owner和group定义改文件的属主和属组;使用validate参数指定文件产生后使用nginx -t -c %s命令去做nginx文件的语法验证;notify是触发handle,如果同步后,文件的MD5值有变化会触发Restart Nginx Service这个handle
  • 第10-12行:定义一个handle状态让nginx服务重启,handle的名称是Restart Nginx Service
  1. hosts文件
[nginx]
192.168.1.201
192.168.1.203
  1. 进行语法检查
ansible-playbook nginx.yml --syntax-check
  1. 查看运行的主机和任务
ansible-playbook nginx.yml --list-task
ansible-playbook nginx.yml --list-hosts
  1. 执行
ansible-playbook nginx.yml
执行

例子2:任务形式

任务目标

服务器:3台
任务1:安装redis
任务2:修改redis.conf配置文件,指定bind绑定各自的局域网ip(用到template模板)
任务3:启动redis服务
####目录结构
目录结构

p.yml文件内容

- hosts: web  #web组三台服务器,hosts文件配置
  remote_user: root  #远端服务器以root权限执行
  tasks:
  - name: install_redis  #任务1,下载redis
    yum: name=redis
    tags: install  #指定标签,暂时不用
  - name: copy_file  #任务2,将本地的redis.conf文件复制到远端服务器
    template: dest=/etc/redis.conf src=redis.conf.j2  #这个j2文件内下一步再看
    tags: copy
  - name: start_redis  #任务3,启动redis
    service: name=redis state=started
    tags: start  #指定标签,暂时不用

redis.conf.j2文件内容

首先复制/etc/redis.conf文件到当前文件夹下的templates目录下且命名为 redis.conf.j2。然后编辑改文件内容,文件部分内容看截图。

mkdir templates  #新建一个templates文件夹
copy /etc/redis.conf ./templates/redis.conf.j2  #复制配置文件
vim ./templates/redis.conf.j2

redis.conf.j2文件内容
ansible_default_ipv4.address 获取每个远程服务器的ipv4内网ip(使用setup模块)

执行文件

ansible-plyabook p.yml

常用的一些复杂变量

playbook中的一些复杂变量

---
# 目标主机支持所有的ad-hoc模式的所有的patterns
- hosts: 172.16.102.109:172.16.102.119
  # 远程ssh认证用户
  remote_user: root
  # 设置playbook的sudo操作
  sudo: yes
  # 设置playbook的sudo操作
  sudo_user: admin
  # 设置facts信息收集
  gather_facts: no
  # 设置accelerate模式(守护进程)
  accelarate: no
  # 设置accelerate端口
  accelerate_port: 5099
  # 设置playbook的tasks失败的百分比
  max_fail_percentage: 30
  # 远程连接的方式
  connection: local
  # 设置并发数目
  serial: 15
  # 设置额外的变量
  vars:
    nginx_port: 80
  # 引入变量文件
  vars_files:
    - "vars.yml"
    - ["one.yml","two.yml"]
  # 设置引入角色
  roles:
    - docker
  # 设置引入tasks
  tasks:
    - include: tasks.yml
  # 设置运行之后的tasks
  post_tasks:
    - name: post_tasks
  # 设置playbook的handlers
  handlers:
    - include: handlers.yml

角色roles

ansible_playbook是ansible进行配置管理的组件,由于ad-hoc命令无法支撑复杂的配置管理工作,在我们实际使用ansible的工作中,大部分时间都是在编写playbook。
简单的安装nginx的脚本

# mkdir -p /data/nginx_install/roles/nginx/tasks
# vim /data/nginx_install/site.yml
---
- hosts: all
  gather_facts: no
  roles:
    - nginx # 与文件同级的目录roles下面的目录
    # - mysql
    # - php
    # - java
# vim /data/nginx_install/roles/nginx/tasks/main.yml
---
- name: install nginx
  yum: name=nginx state=present
- name: start service nginx
  service: name=nginx state=started
# vim /data/nginx_install/hosts
[nginx]
192.168.1.201

目录架构:

roles/
-- nginx  # 角色目录
-- files # 角色的file目录
    -- index.html # 你的file资源
-- handler
    -- main.yml
-- tasks
    -- main.yml #nginx这个角色的入口文件
-- templates
    -- nginx.conf.j2
site.yml  # playbook统一入口文件

引入自定义变量

通过inventory文件来定义变量

# vim hosts
[nginx]
192.168.1.201
192.168.1.203
[nginx:vars]
key1= "i am key1" 

# vim var.yml
---
key3: "i am key3"
value3: "i am value"

# vim vars.yml
---
- hosts: all
  gather_facts: no
  vars:    
    key2: "i am key2"
  vars_files:
    - var.yml
  tasks:
    # 方式一:在hosts文件中引入自定义变量
    - name: display key1    
      debug: msg="{{key1}}"
    # 方式二:在playbook文件中引入自定义变量
    - name: display key2
      debug: msg="{{key2}}"
    # 方式三:通过yml文件引入自定义变量
    - name: display keys
      debug: msg="{{key3}}----->{value3{}}"

# 执行
ansible-playbook -i hosts vars.yml

# 通过命令传入需要加参数-e,这种方法很少用
# 在playbook文件里面使用var_files来传变量

变量传递

使用register变量,在playbook中的task之间进行变量的传递,比如两个tasks,第二个task需要第一个task运行后的结果

---
- hosts: all
  gather_facts: no
  tasks:
    # 第一个task:将命令hostname运行结果赋值给变量info
    - name: register var 
      shell: hostname
      register: info
    # 第二个task:将变量info的内容打印出来,同时打印字典中的key=stdout的值
    - name: display var
      debug: msg="{{info}}"
    - name: display hostname
      debug: msg="{{info.stdout}}"

基本的循环loops

使用with_items这个关键字就可以完成迭代一个列表,列表中的每个变量都叫做item

# 标准的loops(循环列表)
---
- hosts: all
  gather_facts: no
  tasks:
    - name: debug loops
      debug: msg="{{item}}"   # 等同于"for item in with_items:",其中item、with_items是固定写法
      with_items:
        - one
        - two
        - three
        - four
# 批量安装软件
---
- hosts: all
  gather_facts: no
  tasks:
    - name: debug loops
      yum: name="{{item}}" state=present
      with_items:
        - nginx
        - php
        - mariadb
        - zabbix

# 循环字典
---
- hosts: all
  gather_facts: no
  tasks:
    - name: debug dic
      debug: msg="name-->{{item.key}}    val-->{{item.value}}"
      with_items:
        - {key: "one",value: "value_one"}
        - {key: "two",value: "value_two"}

嵌套循环

嵌套loops主要用于一对多或者多对多的合并

---
- hosts: all
  gather_facts: no
  tasks:
    - name: loops in loops
      debug: msg="List1-->{{item[0]}}    List2-->{{item[1]}}"
      with_nested:
        - ["a","b","c"]
        - [1,2,3,4,5]

文件循环和散列循环

散列loops相比标准的loops,变量支持的数据结构更丰富,例如标准的最外层数据必须是python的list数据类型,而散列loops直接支持yaml格式的数据变量

---
- hosts: all
  gather_facts: False
  vars:
    user:
      shan:
        name: shan
        shell: bash
      ceshi:
        name: ceshi
        shell: ssh
  tasks:
    - name: debug loops
      debug: msg="{{item.key}}:{{item.value}}"
      with_dict: "{{user}}"
# 而python实现的方法
# user={'shan':{'name':'shan','shell':bash'},'ceshi':{'name':'ceshi','shell':'ssh'}}
# for i,x in user.items():
#     print i,x[name],x[shell]

文件匹配循环

---
- hosts: all
  gather_facts: False
  tasks:
    - name: debug loops
      debug: msg="{{item}}"
      with_fileglob:
        - /etc/*.conf   # 输出/etc目录下的所有conf文件

条件判断循环以及组合使用

有时候执行一个tasks之后,我们需要检测这个tasks的结果是否达到了预想状态,如果没有达到我们预想的状态时,就需要退出整个playbook执行,这时候需要对这个task结果一直循环检测。
例如:5秒执行一次cat /root/ansible,将结果赋值给变量hosts,然后判断hosts.stdout.startswitch的内容是否以Master字符串开头,如果条件成立,此task运行完成,否则5秒后重试,5次后还不成立,此task运行失败。

---
- hosts: all
  gather_facts: False
  tasks:
    - name: debug IF
      shell: cat /root/ansible
      register: hosts
      until: hosts.stdout.startswith("Master")
      retries: 5
      delay: 5

例如:要么循环5次,要么5次循环还没跑完间隔10s, 但是result.stdout中找到 “all systems go”这几个字母,然后这个task就不跑了(默认的retries=3, delay=5)

---
- hosts: all
  gather_facts: False
  tasks:
  - action: shell /usr/bin/foo
    register: result
    until: result.stdout.find("all systems go") != -1
    retries: 5
    delay: 10

For循环的使用

---
- hosts: all
  gather_facts: no
  tasks:
    - name: debug loops
      shell: "{{item}}"
      with_items:
        - hostname
        - uname
      register: ret
    - name: display ret
      debug: msg="{% for i in ret.results %} {{ i.stdout }} {% endfor %}"

when语句逻辑判断

ansible支持从外部拉取信息,比如可以从数据库里面读取信息然后定义给一个变量的形式,这就是ansible的lookups插件。ansible中所有的conditionals都是使用when来判断的,when值是一个条件表达式,如果天剑判断成立,这个task就执行某个操作,否则不执行或者跳过。

---
- hosts: all
  tasks:
    # 判断IP是否为192.168.1.201
    - name: IP192.168.1.201 run this task
      debug: msg="{{ansible_default_ipv4.address}}"
      when: ansible_default_ipv4.address=="192.168.1.201"
    # 判断主机名是否为inode203
    - name: all host run this task
      shell: hostname
      register: info
    - name: Hostname is inode203 run this task
      debug: msg="{{ansible_fqdn}}"
      when: info['stdout']=="inode203"
    # 判断主机名中是否包含特定字符207
    - name: Hostname is startswith M run this task
      debug: msg="{{ansible_fqdn}}"
      when: info['stdout'].find("207") != -1

Centos系统,版本号为7,满足这两个条件会在 /home/创建123.test 文件

- name: touch centos
  command: touch /home/123.test
  when:
    - ansible_distribution == "CentOS"
    - ansible_distribution_major_version == "7"
  tags:
  - touch_Cent

组条件判断:

# 使用括号对条件进行分组
---
- hosts: localhost
  gather_facts: yes

  tasks:
    - name: "shut down CentOS 6 and Debian 7 systems"
      command: ls -a
      when: (ansible_facts['distribution'] == "CentOS" and ansible_facts['distribution_major_version'] == "6") or
            (ansible_facts['distribution'] == "Debian" and ansible_facts['distribution_major_version'] == "7")
# 所有需要为true的多条件判读(逻辑“and”) 也可以指定为列表
---
- hosts: localhost
  gather_facts: yes

  tasks:
    - name: "shut down CentOS 7 systems"
      command: ls -a
      when:
        - ansible_facts['distribution'] == "CentOS"
        - ansible_facts['distribution_major_version'] == "7"

when判断是centos还是Ubuntu系统后在安装http

- hosts: all
  tasks:
    - name: "update apache version - yum"
      yum: name=httpd state=present
      when:
        - ansible_pkg_mgr == 'yum'
      notify: restart httpd

    - name: "Update apache version - apt"
      apt: name=apache2 state=present update_cache=yes
      when:
        - ansible_pkg_mgr == 'apt'
      notify: restart apache2
 handlers:
    - name: restart apache2
      service: name=apache2 state=restarted
    - name: restart httpd
      service: name=httpd state=restarted

when语句中也可以使用过滤器。如,我们想跳过一个语句执行中的错误,但是后续的任务的执行需要由该任务是否成功执行决定:

    tasks:
      - command: /bin/false
        register: result
        ignore_errors: True
      - command: /bin/something
        when: result|failed
      - command: /bin/something_else
        when: result|success
      - command: /bin/still/something_else
        when: result|skipped

更多内容参考https://www.cnblogs.com/gzxbkk/p/7608055.html

ansible(自动部署lnmp+hdwiki)

  1. 架构与流程
    在做一个比较复杂的事情的时候,需要理清思路,通过画图将思路理清楚,可以使用思维导图
    架构与流程
  2. 写代码
cd /data
mkdir hdwiki
cd hdwiki
vim site.yml #编辑site.yml
mkdir roles
cd roles
mkdir nginx mysql hdwiki base -p

# 角色base:配置yum源
cd /data/roles/base
mkdir tasks files
cd /data/roles/base/files  # 将yum源的配置文件development.repo放在此目录下
cd /data/roles/base/tasks
vim main.yml

# 设置主机清单
cd /data/roles/hdwiki
vim hosts
    [hdwiki]
    192.168.1.201
    192.168.1.203
    [hdwiki:vars]
    nginx=nginx-1.4.4-1.x86_64.rpm
    php=php-5.3.10-1.x86_64.rpm
    user=hdwiki
    password=wiki123456
    database=wiki

# 角色nginx
cd /data/roles/nginx
mkdir tasks files
cd /data/roles/nginx/tasks
vim main.yml

# 角色mysql
cd /data/roles/mysql
mkdir tasks files
cd /data/roles/mysql/tasks
vim main.yml

# 角色hdwiki
cd /data/roles/hdwiki
mkdir tasks files
cd /data/roles/hdwiki/tasks
vim main.yml

# 执行
cd /data/roles/hdwiki
ansible-playbook -i hosts site.yml

playbook文件

# 总入口文件site.yml
---
- hosts: all
  roles:
    - base
    - nginx
    - mysql
    - hdwiki

# base的入口文件main.yml
---
- copy: scr=development.repo dest=/etc/yum.repos.d/development.repo
- shell: yum makecache

# nginx的入口
---
- yum: name={{item}} state=present
  with_items:
    - libtool-libs
    - libXpm-devel
    - fontconfig-devel
    - libpng
    - libjpeg-turbo
- shell: mkdir -p /usr/local/services
- name: copy nginx rpm
  copy: src={{nginx}} dest=opt/{{nginx}}
- shell: rpm-qa| grep nginx || rpm -ivh /opt/{{nginx}}  # 已经安装了的机器不需要再安装
- service: name=nginx state=restarted
- copy: src={{php}} dest=opt/{{php}}
- shell: rpm-qa| grep php || rpm -ivh /opt/{{php}}
- service: name=php-fpm state=restarted

# mysql的入口
---
- yum: name={{item}} state=present
  with_items:
    - mysql-server
    - mysql-python
- service: name=mysqld state=started
- mysql_db: name={{database}} state=present
- mysql_user: name={{user}} password={{password}} priv=*.*:ALL host="%" state=present
 - mysql_user: name={{user}} password={{password}} priv=*.*:ALL host="localhost" state=present

# hdwiki的入口
---
- unarchive: src=wiki.tar.gz dest=/data/htdocs/www
- shell: chown -R www:www /data/htdocs/www

ansible-jinja2

jinja2是ansible的默认的模板语音,jinjia2是目前比较流行的一款模板语音,ansible默认支持jinja2语言内的filter,jinja2也提供了很多filter。
字符串过滤器

  • safe:禁止转义,渲染时不会转义特殊字符
  • capitallize:把首字母转大写,其他的字母转小写
  • lower:把所有的字母转小写
  • upper:把所有字母转大写
  • title:把每个单词的首字母转大写
  • trim:去掉首尾空格
  • striptags:去掉所有的HTML标签
  • replace:替换字符串的值
  • round:对数字四舍五入
  • int:转换成int类型
  • default:定义变量的默认值
  • reverse:字符串反转
  • format:格式化输出,例如{{ '%s is %d' | format("Number", 2) }},返回值"Number is 2"
  • abs:绝对值
    列表过滤器
  • first:获取列表的第一个元素
  • last:获取列表的最后一个
  • sort:排序
  • sum:列表求和
  • length:求列表长度
  • join:将多个值拼接成字符串,类似python的join()函数
  • max:最大值
  • min:最小值
  • random:获取随机值
    列表中所有元素都全大写。这里可以用upper,lower,但capitalize无效
    更多内容参考
    https://jinja.palletsprojects.com/en/2.10.x/
    https://www.cnblogs.com/mauricewei/p/10056379.html
---
- hosts: all
  gather_facts: False
  vars:
    list: [1,2,3,4,5]
    one: "1"
    str: "string"
  tasks:
    # 执行命令,并将结果赋值给变量info
    - name: run commands
      shell: df -h
      register: info
    # 对变量info使用pprint进行格式化,debug时使用可以打印变量的详细信息
    - name: debug pprint filter
      debug: msg="{{info.stdout|pprint}}" 
    - name: debug info
      debug: msg="{{info}}" 
    # 判断info[changed]值,如果为true,则执行debug
    - name: debug conditionals filter
      debug: msg="the run commands status is changed"
      when: info.changed
    # 对变量one的值进行int转变,对变量str的值进行首字母大写操作
    - name: debug int capitalize filter
      debug: msg="The int value {{one|int}}. The lower value is {{str|capitalize}}"
    # 如果ansible变量定义了就引用其值,否则就定义其默认值为"ansible is not define"
    - name: debug default filter
      debug: msg="The variable value is {{ansible|default('ansible is not define')}}"
    # 获取变量列表list中的最大值和最小值
    - name: debug list max and min filter
      debug: msg="The list max value is {{list|max}} and min value is {{list|min}}"
      # 随意选取一个list内的值,然后随机生成一个1000以内的数据,以1开始且步长为10
    - name: debug random filter
      debug: msg="The list random value is {{list|random}} and generate a random value is {{1000|random(1,10)}}"
    # 将list内的值连接在一起形成字符串
    - name: debug join filter
      debug: msg="The join filter value is {{list| join("+")}}"
    # 将变量str中的字母t替换为T
    - name: debug replace  filter
      debug: msg="The replace value is {{str|replace('t','T')}}"
    # string==>strawen,将string变量ri以及之后的所有字符替换为awen
    - name: debug regex_replace filter
      debug: msg="The regex_replace value is {{str|regex_replace(('ri(.*)$'),'awen')}}"

循环与判断

---
- hosts: all
  gather_facts: no
  tasks:
    # 解析之后执行,执行结果写入被控端/tmp/f2
    - template: src=f2.j2  dest=/tmp/f2

f2.j2文件

{% set list=['one','two','three'] %}
{% set dict={'1':'one','2':'two','3':'three'} %}
{% set dict1={'1':{'1.1':{'1.2':'one point two'}} %}
# 循环
{% for i in list %}
    {{i}}
{% endfor %}
{% for key,value in dict.iteritems() %}
    {{key}}--->{{value}}
{% endfor %}
# 判断
{% for i in list %}
    {% if i=='one' %} # 值为one则打印
        --->{{i}}   
    {% elif loop.index==2 %}  # 索引为2(two)则打印
         --->>{{i}}
    {% else %}
        --->>>{{i}}
    {% endif %}
{% endfor %}

# 多层嵌套字典取值
{{dict1['1']['1.1']['1.2']}}

jinja2中可以使用set定义临时变量也可以直接使用ansible其他地方定义的变量,关于jinja2变量的引用都是采用{{变量名}}的方式,当然里面还可以根据变量名数据类型选择你想要的信息,比如dict={'key':'value'},直接{{dict}}会返回一个python的字典数据,如果只需要key对应的值,则需要{{dict['key']}}{{dict.get('key')}}

ansible-一键部署haproxy+lnmp

首先需要知道我们需要什么样的架构,然后我们了解整个架构每个组件之间是如何衔接和交互的,当然我们还要清除架构中每一个组件的原理和流程。
haproxy是一款提供高可用、负载均衡以及基于TCP和HTTP应用的代理软件,目前很多大公司也在使用其做web集群和cache集群的负载均衡以及代理
整个架构的数据流向:

  1. haproxy代理以及负载的nginx+php
  2. nginx+php的web功能
  3. mysql功能

架构

架构

目录结构及角色

haproxy_lnmp
├── group_vars
│   ├── all
│   ├── haproxy
│   └── mysql
├── hosts
├── roles
│   ├── base
│   │   ├── tasks
│   │   │   └── main.yml
│   │   └── templates
│   │       ├── CentOS-Base.repo
│   │       └── epel.repo
│   ├── haproxy
│   │   ├── tasks
│   │   │   └── main.yml
│   │   └── templates
│   │       └── haproxy.cfg.j2
│   ├── mysql
│   │   ├── tasks
│   │   │   └── main.yml
│   │   └── templates
│   │       └── my.cnf.j2
│   ├── nginx
│   │   ├── tasks
│   │   │   └── main.yml
│   │   └── templates
│   │       ├── index.php.j2
│   │       └── nginx.conf.j2
│   └── php
│       └── tasks
│           └── main.yml
└── site.yml

变量

全局变量

# cat group_vars/all
---
ansible_ssh_pass: P@ssw0rd

mysql变量

# cat group_vars/mysql 
---
user: ansible
password: ansible
database: ansible
mysql_port: 3306

haprxoy变量

# cat group_vars/haproxy 
---
mode: http
listenport: 80

hosts列表

# cat hosts
[nginx]
192.168.1.201
192.168.1.203
[php]
192.168.1.201
192.168.1.203
[mysql]
192.168.1.203
[haproxy]
192.168.1.207

入口文件site.yml

---
- name: Modify the yum
  gather_facts: no
  hosts: all
  remote_user: root
  roles:
    - base

- name: Install the nginx
  hosts: nginx
  remote_user: root
  roles:
    - nginx

- name: Install the mysql 
  gather_facts: no
  hosts: mysql
  remote_user: root
  roles:
    - mysql

- name: Install the php
  gather_facts: no
  hosts: php
  remote_user: root
  roles:
    - php

- name: Install the haproxy
  gather_facts: no
  hosts: haproxy
  remote_user: root
  roles:
    - haproxy

角色

base

入口文件

# cat roles/base/tasks/main.yml 
---
- name: Copy the yum repo to the remote hosts
  template: src={{item}}  dest=/etc/yum.repos.d/{{item}}
  with_items:
      - CentOS-Base.repo
      - epel.repo

yum源文件

yum源文件,可以使用阿里、163或清华大学等源

# ll roles/base/templates/
total 8
-rw-r--r-- 1 root root 1663 Jul 21 17:50 CentOS-Base.repo
-rw-r--r-- 1 root root  970 Jul 21 17:50 epel.repo

# more roles/base/templates/CentOS-Base.repo
[base]
name=CentOS-$releasever - Base - mirrors.aliyun.com
baseurl=http://mirrors.aliyun.com/centos/$releasever/os/$basearch/
enabled=1
gpgcheck=0
[updates]
name=CentOS-$releasever - Updates - mirrors.aliyun.com
baseurl=http://mirrors.aliyun.com/centos/$releasever/updates/$basearch/
enabled=1
gpgcheck=0
[extras]
name=CentOS-$releasever - Extras - mirrors.aliyun.com
baseurl=http://mirrors.aliyun.com/centos/$releasever/extras/$basearch/
enabled=1
gpgcheck=0
[centosplus]
name=CentOS-$releasever - Plus - mirrors.aliyun.com
baseurl=http://mirrors.aliyun.com/centos/$releasever/centosplus/$basearch/
enabled=1
gpgcheck=0
[contrib]
name=CentOS-$releasever - Contrib - mirrors.aliyun.com
baseurl=http://mirrors.aliyun.com/centos/$releasever/contrib/$basearch/
enabled=1
gpgcheck=0

# epel.repo
[epel]
name=Extra Packages for Enterprise Linux 7 - $basearch
metalink=https://mirrors.fedoraproject.org/metalink?repo=epel-7&arch=$basearch
enabled=1
gpgcheck=0
[epel-debuginfo]
name=Extra Packages for Enterprise Linux 7 - $basearch - Debug
metalink=https://mirrors.fedoraproject.org/metalink?repo=epel-debug-7&arch=$basearch
enabled=1
gpgcheck=0
[epel-source]
name=Extra Packages for Enterprise Linux 7 - $basearch - Source
metalink=https://mirrors.fedoraproject.org/metalink?repo=epel-source-7&arch=$basearch
enabled=1
gpgcheck=0

nginx

入口文件

# cat roles/nginx/tasks/main.yml 
---
- name: Install Nginx Package
  yum: name=nginx state=present

- name: Copy Nginx.conf
  template: src=nginx.conf.j2 dest=/etc/nginx/nginx.conf owner=root group=root mode=0644 validate='nginx -t -c %s'  

- name: restart nginx
  service: name=nginx state=restarted enabled=yes

- name: Copy php file to nginx web root
  template: src=index.php.j2 dest=/usr/share/nginx/html/index.php

配置文件

nginx配置文件是同版本的配置文件

# ll  roles/nginx/templates/
total 8
-rw-r--r-- 1 root root   34 Jul 21 19:21 index.php.j2
-rw-r--r-- 1 root root 2491 Jul 21 17:50 nginx.conf.j2

# cat roles/nginx/templates/index.php.j2 
# 网页文件,获取nginx主机的IP
{{ansible_default_ipv4.address}}

# cat roles/nginx/templates/nginx.conf.j2
user nginx;
worker_processes auto;
error_log /var/log/nginx/error.log;
pid /run/nginx.pid;
include /usr/share/nginx/modules/*.conf;
events {
    worker_connections 1024;
}
http {
    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';
    access_log  /var/log/nginx/access.log  main;
    sendfile            on;
    tcp_nopush          on;
    tcp_nodelay         on;
    keepalive_timeout   65;
    types_hash_max_size 2048;
    include             /etc/nginx/mime.types;
    default_type        application/octet-stream;
    include /etc/nginx/conf.d/*.conf;
    server {
        listen       80 default_server;
        listen       [::]:80 default_server;
        server_name  _;
        root         /usr/share/nginx/html;
        include /etc/nginx/default.d/*.conf;
        location / {
        }
        error_page 404 /404.html;
            location = /40x.html {
        }
        error_page 500 502 503 504 /50x.html;
            location = /50x.html {
        }
    }
}

php

# cat roles/php/tasks/main.yml 
---
- name: Install php
  yum: name={{item}} state=installed
  with_items:
    - php-fpm
    - php-mysql

- name: start php service 
  service: name=php-fpm state=started enabled=yes

mysql

入口文件

# cat roles/mysql/tasks/main.yml 
---
- name: Install mysql
  yum: name={{item}} state=installed
  with_items:
    - mariadb-server
    - MySQL-python

- name: Configure the mysql cnf file with hosts
  template: src=my.cnf.j2 dest=/etc/my.cnf owner=root group=root mode=0644

- name: Start mariadb service 
  service: name=mariadb state=started enabled=yes

- name: Create database
  mysql_db: name={{database}} state=present
  
- name: Create user
  mysql_user: name={{user}} password={{password}} priv={{database}}.*:ALL host='%' state=present

配置文件

# cat roles/mysql/templates/my.cnf.j2 
[mysqld]
datadir=/var/lib/mysql
socket=/var/lib/mysql/mysql.sock
symbolic-links=0
port={{ mysql_port }}
[mysqld_safe]
log-error=/var/log/mysqld.log
pid-file=/var/run/mariadb/mysqld.pid

haproxy

入口文件

# cat roles/haproxy/tasks/main.yml 
- name: Install haproxy
  yum: name=haproxy state=present
 
- name: Configure the haproxy cnf file with hosts
  template: src=haproxy.cfg.j2 dest=/etc/haproxy/haproxy.cfg

- name: restart haproxy
  service: name=haproxy state=restarted enabled=yes

配置文件

# cat roles/haproxy/templates/haproxy.cfg.j2 
global
    log         127.0.0.1 local2
    chroot      /var/lib/haproxy
    pidfile     /var/run/haproxy.pid
    maxconn     4000
    user        haproxy
    group       haproxy
    daemon
    stats socket /var/lib/haproxy/stats
defaults
    mode                    {{mode}} 
    log                     global
    option                  httplog
    option                  dontlognull
    option http-server-close
    option forwardfor       except 127.0.0.0/8
    option                  redispatch
    retries                 3
    timeout http-request    10s
    timeout queue           1m
    timeout connect         10s
    timeout client          1m
    timeout server          1m
    timeout http-keep-alive 10s
    timeout check           10s
    maxconn                 3000
frontend  main *:{{listenport}}
    acl url_static       path_beg       -i /static /images /javascript /stylesheets
    acl url_static       path_end       -i .jpg .gif .png .css .js
    use_backend static          if url_static
    default_backend             app
backend static
    balance     roundrobin
    server      static 127.0.0.1:8080 check
backend app
    balance     roundrobin
    {% for host in groups['nginx'] %}
    server {{hostvars[host]['inventory_hostname']}} {{host}}:80}
    # server {{hostvars[host]['ansible_hostname']}} {{hostvars[host]['inventory_hostname']}}:80 check inter 3000 rise 3 fall 2
    {% endfor %}

语法检查

只检查site.yml入口文件,不能检查各个角色下的main.yml

ansible-playbook site.yml --syntax-check
    playbook: site.yml

执行

ansible-playbook -i hosts site.yml

测试

curl 192.168.1.201
curl 192.168.1.203
curl 192.168.1.207
curl 192.168.1.207
curl 192.168.1.207
curl 192.168.1.207

排错

在入口文件site.yml中的nginx角色没有使用gather_facts: no选项,是因为index.php中要获取主机的信息得到IP地址。

ansible优化执行速度

开启ssh长连接

ansible模式是使用ssh和被管理机器进行通信的,所以ansible对ssh的依赖非常强。
ansible在openssh5.6版本以后可以支持multiplexing,在ansible.cfg文件中设置ssh长连接,设置参数如下:

# vim /etc/ansible/ansible.cfg
sh_args = -C -o ControlMaster=auto ControlPersist=5d  
# ControlPersist=5d :设置整个长连接保持时间为5天,如果开启后通过ssh连接过的设备都会在当前ansible/cp目录下产生一个socket文件,也可以通过netstat命令查看,会发现有一个状态为ESTABLISHED的连接一直与被控端进行TCP连接

开启pipelining

pipelining也是openssh的一个特性,之前的流程是在本地产生一个py的文件,然后put到被控端上面再执行这个py脚本。如果开启pipelining,这个过程将会在ssh的会话中进行。
如果需要开启pipelining,则需要在被控端/etc/sudoers文件编辑当前ansible ssh用户的配置为requirety。

# vim /etc/ansible/ansible.cfg
pipelining = True

开启accelerate

和前面的ssh的长连接有点类似,因为都依赖ansible控制端和被控端有一个长连接,但是accelerate是使用python在被控端运行一个守护进行,然后ansible会通过这个守护进行监听的端口进行通信,开启accelarate模式,则需要在ansible控制端和被控端都安装python-keyczar软件包

# vim /etc/ansible/ansible.cfg
[accelerate]
accelerate_port=5099
accelerate_timeout=30
accelerate_connect_timeout=5.0

设置facts缓存

在playbook里面有一个gather_facts:yes选项,用来收集远端机器的信息,比较浪费时间,执行一次之后,将结果保存一定时间可以节省后面执行playbook的时间,从而提高效率

# vim /etc/ansible/ansible.cfg
gathering = smart
fact_caching_timeout = 86400
fact_caching = jsonfile
fact_cashing_connection = /dev/shm/ansible_fact_cache 
 # ansible支持将数据放到redis里面,读取速度会更快
gathering = smart
fact_caching_timeout = 86400
fact_caching = redis

ansible目录标准化

参考https://docs.ansible.com/ansible/latest/user_guide/playbooks_best_practices.html
官网最佳实践中推荐使用的ansible工作目录的结构,统一工作目录如下:

Project
├── hosts # 主机清单
├── site.yml # playbook统一入口文件
├── webservers.yml # 特殊任务playbook文件【一般不用】
├── java  # java环境的inventory文件
├── web # web环境的inventory文件
├── library # 自定义模块存放的目录
├── filter_plugins # 自定义filter插件存放的目录
├── group_vars  # 自定义变量存放文件夹
│   ├── all  # 全局变量
│   ├── role1 # role1使用的变量
│   └── role2
├── host_vars  # 自定义变量存放文件夹,功能同group_vars【一般不用】
│   ├── all  # 全局变量
│   ├── host1 # role1使用的变量
├── roles #角色存放的目录
│   ├── role1  # 角色1目录
│   │   ├── tasks # 存放playbook的目录,其中main.yml是主入口文件
│   │   │   └── main.yml # 在main.yml中导入其他yml文件,要采用import_tasks关键字
│   │    |   └── install.yml
│   │   ├── handles # 存放tasks中的notify指定的内容
│   │   │   └── main.yml
│   │   ├── files # unarchive、copy等模块会来这找文件,不必写绝对路径,只需写文件名
│   │    |    ├── mysql.tar.gz
│   │    |    └── nginx.tar.gz
│   │   └── templates # template模块需要用的文件存放目录,存放的是软件的配置文件
│   │         ├── index.php.j2 # 使用jinja2格式作为文件模板,进行文档内变量替换的模块
│   │         └── nginx.conf.j2

# 举例:
haproxy_lnmp
├── group_vars
│   ├── all
│   ├── haproxy
│   └── mysql
├── hosts
├── roles
│   ├── base
│   │   ├── tasks
│   │   │   └── main.yml
│   │   └── templates
│   │       ├── CentOS-Base.repo
│   │       └── epel.repo
│   ├── haproxy
│   │   ├── tasks
│   │   │   └── main.yml
│   │   └── templates
│   │       └── haproxy.cfg.j2
│   ├── mysql
│   │   ├── tasks
│   │   │   └── main.yml
│   │   └── templates
│   │       └── my.cnf.j2
│   ├── nginx
│   │   ├── tasks
│   │   │   └── main.yml
│   │   └── templates
│   │       ├── index.php.j2
│   │       └── nginx.conf.j2
│   └── php
│       └── tasks
│           └── main.yml
└── site.yml

ansible-项目实战-批量部署

需求和目的

通过ansible制作一个初始化脚本,采用标准化半自动化流程,可移植、可复用、步骤明确、操作简便、低故障、责任明确。

初始化操作

  1. 配置yum源
  2. 安装常用软件和机器所需软件:wget、vim、unzip、net-tools、gcc、ntpdate等
  3. 配置防火墙
  4. 配置SeLinux(关闭)
  5. 生成秘钥,发送公钥
  6. 修改ssh配置文件,修改ssh连接的默认端口
  7. 配置DNS、hostname、IP等
  8. 配置时间同步服务器,添加到crontab
  9. 调优,优化内核参数:提高打开文件数量、配置nginx最大连接数等等
  10. IO测试,磁盘性能
  11. ......

创建所有的角色

  1. base,替换yum源
  2. public,秘钥
  3. yum,安装依赖
  4. crontab,配置时间服务器等
  5. iptables,关闭selinux、NetworkManager和iptables配置
  6. init,调优初始化
  7. install,安装软件
  8. ssh_config,修改ssh的配置
mkdir -p setup_init/roles/{base,public,yum,crontab,iptables,init,install,ssh_config}/{tasks,templates,files}

开发

目录结构

/root/
└── setup_init
    └── roles
        ├── base
        │   ├── files
        │   │   ├── CentOS-Base.repo
        │   │   └── epel.repo
        │   ├── tasks
        │   │   └── main.yml
        │   └── templates
        ├── crontab
        │   ├── files
        │   ├── tasks
        │   │   └── main.yml
        │   └── templates
        ├── init
        │   ├── files
        │   ├── tasks
        │   │   └── main.yml
        │   └── templates
        ├── install
        │   ├── files
        │   ├── tasks
        │   │   └── main.yml
        │   └── templates
        ├── iptables
        │   ├── files
        │   ├── tasks
        │   │   └── main.yml
        │   └── templates
        ├── public
        │   ├── files
        │   │   └── id_rsa.pub
        │   ├── tasks
        │   │   └── main.yml
        │   └── templates
        ├── ssh_config
        │   ├── files
        │   ├── tasks
        │   │   └── main.yml
        │   └── templates
        └── yum
            ├── files
            ├── tasks
            │   └── main.yml
            └── templates

入口文件

base/tasks/main.yml

---
- copy: src=CentOS-Base.repo dest=/etc/yum.repos.d/CentOS-Base.repo
- copy: src=epel.repo dest=/etc/yum.repos.d/epel.repo
- shell: yum makecache

public/tasks/main.yml

---
- shell: mkdir -p ~/.ssh
- copy: src=id_rsa.pub dest='~/.ssh/id_rsa.pub_tmp' force=yes
- shell: cat '~/.ssh/id_rsa.pub_tmp' >>'~/.ssh/zuthorized_keys'

yum/tasks/main.yml

---
- yum: name={{item}} state=present
  with_items:
    - vim
    - unzip
    - wget
    - gcc
    - net-tools
    - tree
    - ntpdate

crontab/tasks/main.yml

---
- cron: minute */2 hour=* day=* month=* name=uptime job='ntpdate time.windows.com >/dev/null' state=present

iptables/tasks/main.yml

---
- serivce: name=NetworkManager state=stopped enabled=no
- shell: setenforce 0
# 防火墙根据要求进行配置

init/tasks/main.yml

---
# 根据实际情况进行优化

install/tasks/main.yml

---
# 根据实际情况进行安装