介绍

1、简介

ansible是新出现的自动化运维工具,基于Python开发,集合了众多运维工具(puppet、cfengine、chef、func、fabric)的优点,实现了批量系统配置、批量程序部署、批量运行命令等功能。

ansible是基于模块工作的,本身没有批量部署的能力。真正具有批量部署的是ansible所运行的模块,ansible只是提供一种框架。主要包括:

  • 连接插件connection plugins:负责和被监控端实现通信;

  • host inventory:指定操作的主机,是一个配置文件里面定义监控的主机;

  • 各种模块核心模块、command模块、自定义模块;

  • 借助于插件完成记录日志邮件等功能;

  • playbook:剧本执行多个任务时,非必需可以让节点一次性运行多个任务。

2、特性

  • no agents:不需要在被管控主机上安装任何客户端;

  • no server:无服务器端,使用时直接运行命令即可;

  • modules in any languages:基于模块工作,可使用任意语言开发模块;

  • yaml,not code:使用yaml语言定制剧本playbook;

  • ssh by default:基于SSH工作;

  • strong multi-tier solution:可实现多级指挥。

3、优点

  • 轻量级,无需在客户端安装agent,更新时,只需在操作机上进行一次更新即可;

  • 批量任务执行可以写成脚本,而且不用分发到远程就可以执行;

  • 使用python编写,维护更简单,ruby语法过于复杂;

  • 支持sudo。

4、基本架构

核心引擎:即ansible

核心模块(core modules):这些都是ansible自带的模块,ansible模块资源分发到远程节点使其执行特定任务或匹配一个特定的状态。

自定义模块(custom modules):如果核心模块不足以完成某种功能,可以添加自定义模块。

插件(plugins):完成模块功能的补充,借助于插件完成记录日志、邮件等功能

剧本(playbook):定义ansible任务的配置文件,可以将多个任务定义在一个剧本中,由ansible自动执行,剧本执行支持多个任务,可以由控制主机运行多个任务,同时对多台远程主机进行管理。

playbook是ansible的配置、部署和编排语言,可以描述一个你想要的远程系统执行策略,或一组步骤的一般过程。如果ansible模块作为你的工作室工具,playbook就是设计方案。在基本层面上,剧本可以用于管理配置和部署远程机器。在更高级的应用中,可以序列多层应用及滚动更新,并可以把动作委托给其他主机,与监控服务器和负载平衡器交互。

连接插件(connection plugins):ansible基于连接插件连接到各个主机上,负责和被管理节点实现通信。虽然ansible是使用ssh连接到各被管理节点,但它还支持其他的连接方法,所以需要有连接插件。

主机清单(host inventory):定义ansible管理的主机策略,默认是在ansible的hosts配置文件中定义被管节点,同时也支持自定义动态主机清单和指定配置文件路径。

ansible采用paramiko协议库(Fabric也使用这个),通过ssh或者ZeroMQ等连接主机。ansible在控制主机主机将ansible模块通过ssh协议(或者Kerberos、LDAP)推送到被管节点执行,执行完之后自动删除。控制主机与被管理节点之间支持local、SSH、ZeroMQ三种连接方式,默认使用基于SSH的连接。在规模较大的情况下使用ZeroMQ连接方式会明显改善执行速度。

5、任务执行模式

ansible系统由控制主机对被管节点的操作方式可分为两类,即ad-hoc和playbook。

ad-hoc模式使用单个模块,支持批量执行单条命令。

playbook模式是ansible的主要管理方式,通过多个task集合完成一类功能,可以简单的理解为通过组合多条ad-hoc操作的配置文件。

6、 ansible与其他配置管理软件的对比

项目 Puppet Saltstack Ansible
开发语言 Ruby Python Python
是否有客户端
是否支持二次开发 不支持 支持 支持
服务器与远程机器是否相互验证
服务器与远程机器通信是否加密 是,标准SSL协议 是,使用AES加密 是,使用OpenSSH
是否提供WEB UI 提供 提供 提供,但是商业版本
配置文件格式 Ruby语法 YAML YAML
命令行执行 不支持,但可以通过配置模块实现 支持 支持

Ansible 配置

在Ansible中,几乎所有配置都可以通过Ansible的Playbook或环境变量来重新赋值。在运行Ansible命令时,如果不指定inventory命令将会按照预先设定的顺序查找配置文件

1)ANSIBLE_CONFIG:首先,Ansible命令会检查环境变量,及这个环境变量将指向的配置文件

2)./ansible.cfg:其次,将会检查当前目录下的ansible.cfg配置文件

3)~/.ansible.cfg:再次,将会检查当前用户home目录下的.ansible.cfg配置文件

4)/etc/ansible/ansible.cfg:最后,将会检查在用软件包管理工具安装Ansible时自动产生的配置文件

配置项 说明 默认值
inventory ansible inventory文件路径 /etc/ansible/hosts
library ansible模块文件路径 /usr/share/my_modules/
remote_tmp ansible远程主机脚本临时存放目录 ~/.ansible/tmp
local_tmp ansible管理节点脚本临时存放目录 ~/.ansible/tmp
forks ansible执行并发数 5
poll_interval ansible异步任务查询间隔 15
sudo_user ansible sudo用户 root
ask_sudo_pass 运行ansible是否提示输入sudo密码 True
ask_pass 运行ansible是否提示输入密码 True
transport ansible远程传输模式 smart
remote_port 远程主机SSH端口 22
module_lang ansible模块运行默认语言环境 C
gathering facts信息收集开关定义 smart
roles_path ansible role存放路径 /etc/ansible/roles
timeout ansible SSH连接超时时间,单位秒 10
remote_user ansible远程认证用户 root
log_path ansible日志记录文件,执行Ansible的用户需要有写入日志的权限,模块将会调用被管节点的syslog来记录,口令是不会出现的日志中的 /var/log/ansible.log
module_name ansible默认执行模块 command
executable ansible命令执行shell /bin/sh
hash_behaviour ansible主机变量重复处理方式 replace
private_role_vars 默认情况下,角色中的变量将在全局变量范围中可见。 为了防止这种情况,可以启用以下选项,只有tasks的任务和handlers得任务可以看到角色变量 yes
vault_password_file 指定vault密码文件路径
ansible_managed 定义的一个Jinja2变量,可以插入到Ansible配置模版系统生成的文件中 Ansible managed
display_skipped_hosts 开启显示跳过的主机 True
error_on_undefined_vars 开启错误,或者没有定义的变量 False
host_key_checking 如果有台被管节点重新安装系统并在known_hosts中有了与之前不同的密钥信息,就会提示一个密钥不匹配的错误信息,直到被纠正为止,在使用Ansible时,如果有台被管理节点没有在known_hosts中被初始化,将会在使用Ansible或定时执行Ansible时提示对key信息的确认。如果你不想出现这种情况,并且你明白禁用此项行为的含义,只要修改该参数为False即可 False

官方文档:https://docs.ansible.com/ansible/latest/reference_appendices/config.html

ansible hosts配置

  1. 编辑/etc/ansible/hosts
  2. 添加本机的public SSH key到目标机器的authorized_keys #ssh-copy-id
  3. 添加本机的私钥到Ansible
  4. 运行ansible all -m ping 测试是否添加成功

Inventory 分组

Ansible可同时操作属于一个组的多台主机,组和主机之间的关系通过inventory文件配置,默认文件路径为/etc/ansible/hosts

常用参数配置:

参考 解释 例子
ansible_ssh_host 将要连接的远程主机名.与你想要设定的主机的别名不同的话,可通过此变量设置. ansible_ssh_host=192.168.1.123
ansible_ssh_port ssh端口号.如果不是默认的端口号,通过此变量设置. ansible_ssh_port=8000
ansible_ssh_user 默认的 ssh 用户名 ansible_ssh_user=root
ansible_ssh_pass ssh 密码(这种方式并不安全,我们强烈建议使用 --ask-pass 或 SSH 密钥) ansible_ssh_pass=’123456’
ansible_sudo_pass sudo 密码(这种方式并不安全,我们强烈建议使用 --ask-sudo-pass) ansible_sudo_pass=’123456’
ansible_sudo_exe sudo 命令路径(适用于1.8及以上版本) ansible_sudo_exe=/usr/bin/sudo
ansible_connection 与主机的连接类型.比如:local, ssh 或者 paramiko. Ansible 1.2 以前默认使用 paramiko.1.2 以后默认使用 'smart','smart' 方式会根据是否支持 ControlPersist, 来判断'ssh' 方式是否可行. ansible_connection=local
ansible_ssh_private_key_file ssh 使用的私钥文件.适用于有多个密钥,而你不想使用 SSH 代理的情况. ansible_ssh_private_key_file=/root/key
ansible_shell_type 目标系统的shell类型.默认情况下,命令的执行使用 'sh' 语法,可设置为 'csh' 或 'fish'. ansible_shell_type=zsh
ansible_python_interpreter 目标主机的 python 路径.适用于的情况: 系统中有多个 Python, 或者命令路径不是"/usr/bin/python",比如 *BSD, 或者 /usr/bin/python 不是 2.X 版本的 Python.我们不使用 "/usr/bin/env" 机制,因为这要求远程用户的路径设置正确,且要求 "python" 可执行程序名不可为 python以外的名字(实际有可能名为python26). ansible_python_interpreter=/usr/bin/python2.6
ansible_*_interpreter 定义其他语言解释器 ansible_*_interpreter=/usr/bin/ruby
ansible_sudo 定义sudo用户 ansible_sudo=root

注:从ansible2.0开始, ansible_ssh_user, ansible_ssh_host, ansible_ssh_port已经改变为ansible_user, ansible_host, ansible_port。

格式:[组名]

[test]   # 组名

host1 ansible_ssh_port=65522 ansible_ssh_host=10.0.0.1 ansible_ssh_user=root# 别名  host1

连续的主机

[group1]

g[1:50].example.com
g[a-z].example.com

给整个组添加变量 上面是给服务器单独配置变量,但是也可以给整个组配置变量:

[group1:vars]
ansible_connection=ssh
[group1]
host1
host2

只要需要配置的组的名字对上了,就可以获取配置

将一个组变成另一个组的成员:

[groupA]
host1
[groupB:children]
groupA

假设我们有两个服务器。 已经使用ssh-copy-id访问成功:

ssh root@your_server_ip 假定默认组的IP地址是10.10.10.2和 10.10.10.3。

[default]
host1 ansible_ssh_host=10.10.10.2 ansible_ssh_user=centos 
host2 ansible_ssh_host=10.10.10.3 ansible_ssh_user=Ubuntu 

主机可以在多个组中,组可以为其所有成员配置参数。 如果你想为每个服务器指定配置细节,而不管组关联的,你可以在把这些信息在一个文件中/etc/ansible/group_vars/all 。 单个主机可以通过在目录下创建文件进行配置/etc/ansible/host_vars。

使用简单的Ansible命令

通过键入以下命令来ping您配置的所有服务器:

ansible -m ping all
输出
host1 | success >> {
    "changed": false,
    "ping": "pong"
}

host2 | success >> {
    "changed": false,
    "ping": "pong"
}

这是一个基本测试,以确保Ansible已连接到其所有主机。

“all”表示所有主机。 我们可以很容易地指定一个组:

ansible -m ping group1 我们还可以指定单个主机:

ansible -m ping host1 我们可以通过用冒号分隔它们来指定多个主机:

ansible -m ping host1:host2

我们向远程主机发送终端命令并检索结果。 例如,要找出我们的host1机器上的内存使用情况:

ansible -m shell -a 'free -m' host1
输出:
host1 | success | rc=0 >>
             total       used       free     shared    buffers     cached
Mem:          3954        227       3726          0         14         93
-/+ buffers/cache:        119       3834
Swap:            0          0          0

ansible 有两种方法完成任务,一个是as-hoc,一个是playbook,前者解决简单任务,后者复杂任务

Modules

ansible使用模块来完成任务,比如安装软件,复制文件以及使用模板。

Ansible常用模块

ping

检查指定节点机器是否能联通

yum

这个模块是RedHat和CentOS作为远端节点的OS的时候,用得最多的包管理工具

apt

这个模块是ubuntu和Debian作为远端节点的时候用的最多的包管理工具

deb: 软件包名字,可选 install_recommends: 默认为true,设置为False代表只下载不安装 update_cache: yes相当于apt-get update name: apt要下载的软件名,指定版本可用name=git=1.6 state: (present, adsent, latest) present表示为安装,然后是删除,在是安装位最新版本

pip

安装python库依赖项,必须参数为name或者requirements

chdir: 执行pip前需要进入的目录 name: 库名字 requirements: requirements.txt文件路径,应该是远程系统的本地文件,就可以使用chdir将文件指定为相对定位 version: 库版本 extra_args: pip额外参数 executable: 显式可执行文件或可执行文件的路径名 virtualenv: 要安装到的virtualenv路径名 virtualenv_command: 创建虚拟环境 virtualenv_python: 用于创建虚拟环境的命令或路径名 state:(present, lastest, absent)

synchronize

使用rsync同步文件,将主控方目录推送到指定节点的目录下

delete: 删除不存在的文件 src: 要同步到目的地的源主机的路径 dest: 目的地上同步地址 dest_port: 目的地机上的端口 mode: push/pull,默认push,本机向远程传文件 rsync_opts:

copy

在远程主机上面复制文件

src: 复制到远程的文件在本地的地址,如果路径以/结尾,只复制目录里面的内容,如果没有,则包含目录在内的整个内容全部复制 content: 代替src,可以直接设定指定文件的值 dest: 复制到远程文件的路径 directory_mode: 目录权限 force:默认为yes,强制覆盖 others: 所有file模块里面的选项 mode: 0644

user

home:指定用户家目录,需要配合createhome使用 groups: 指定用户组 uid: password: 用户密码,不能使用明文密码 name: 指定用户名 createhome: 是否创建家目录,yes、no system: 是否为系统用户 remove: 当state为absent的时候,remove=yes表示联同家目录一起删除 state: present, absent shell: 指定用户的shell环境 generate_ssh_key: 生成ssh秘钥 ssh_key_bits: 指定创建ssh秘钥中的位数 ssh_key_passphrase: ssh秘钥密码 ssh_key_file: ssh秘钥文件 ssh_key_type: ssh秘钥类型

group

gid: 指定的gid name: 指定用户名 state: present, absent system: true,表示创建的是系统组

service

arguments: 命令选项 enabled: 是否开机启动 yes name: 必选,服务名称 runlevel: 运行级别 sleep: restarted之间的间隔 state: started/stopped/restarted/reloaded

get_url

用于从http,ftp,https服务器上面上下载文件,类似wget

sha256sum: 下载后sha256验证 timeout: 下载超时时间,默认为10s url: 下载的url url_password, url_username: 如果下载需要提供验证 dest: 下载到哪里 headers: 以key:value的格式自定义http标头

file

用于远程主机上面的文件操作

force: 强制创建软连接 group: 文件目录的属组 mode: 文件目录的权限 owner: 文件目录的属主 path: 必选,文件目录的路径 recurse: 递归的设置文件的属性 src: 被连接的源文件路径,当state=link的时候 dest: 被连接到的路径,当state=link的时候 state: directory 如果目录不存在则创建目录 file 如果文件不存在则创建 link 创建软连接 hard 创建硬链接 touch 如果文件不存在则创建,如果存在则修改最后修改时间属性 absent 删除文件目录或者取消连接文件

创建一个目录,如果它不存在
- file:
  path: /etc/some_directory
  state: directory
  mode: 0755

unarchive

解压文件

copy: 解压文件之前,是否现将文件复制到远程主机 creates: 指定一个文件名,当该文件存在,解压不执行 dest: 远程主机上解压文件的绝对路径 list_files: yes会列出压缩包里面的文件 mode: 解压后文件的权限 src: 如果copy为yes,需要指定压缩文件的原路径 group: 解压后文件属组 owner: 解压后文件的属主

shell

chdir: 运行命令前切换到此目录 如果没有模块,我们只能运行shell命令,其实是使用的shell模块:

ansible -i ./hosts remote -b --become-user=root all -m shell -a 'apt-get install nginx’

  • -b 成为另一个用户
  • --become-user=root 以用户root运行命令
  • -m定义模块,这里使用的是shell模块
  • -a给模块传递参数 这样做并不好,因为这样只能完成bash脚本能够完成的任务

我们还可以使用apt模块,这样能确保幂等(一个幂等操作的特点是其任意多次执行所产生的影响均与一次执行的影响相同)

ansible -i ./hosts remote -b --become-user=root -m apt -a 'name=nginx state=installed update_cache=true'

如果执行后显示的是change:False,就表明已经安装,没有对环境造成任何影响

  • name=nginx表示需要安装的软件包名
  • state=installed表示需要的结束状态
  • update_cache=true表示是否更新软件包储存库缓存

通过模块我们可以做我们想做的事,但是为了更好管理,我们把任务都放到playbook里面,就可以运行多个tasks

Playbooks

playbook可以运行多任务还有一些高级功能,playbook和roles(剧本和角色)都使用YAML文件定义: nginx.yml

- hosts: remote
  become: yes
  become_user: root
  tasks:
        - name: install nginx
        apt:
            name: nginx
            state: present
            update_cache: true

YAML文件格式:用缩排方式呈现,结构通过缩进来表示,连续的项目使用-表示,键值对用:表示,不可以使用TAB. 使用playbook运行: ansible-playbook -i ./hosts nginx.yml

Handlers

handlers=tasks,处理程序可以做任务可以完成的任何事,但是只有当另一个task调用它的时候才会执行。 我们添加notify指令到上面的playbook中:

- hosts: remote
  become: yes
  become_user: root
  tasks:
       - name: Install Nginx
       apt:
            name: nginx
            state: present
            update_cache: true
        notify:
            - Start Nginx
  handlers:
        - name: Start Nginx
        service:
            name: nginx
            state: started

任务task运行后会通知名为start Nginx的处理程序:该handler处理使用服务service模块,可以启动停止重启等操作。

所以handler和task的唯一区别就是task会自动调用,handler得等着task来调用它

这里还有一个地方需要注意:如果这里nginx已经安装,那么nginx安装任务不会运行,handler也不会被调用。

使用变量

首先定义变量:

- hosts: local
  connection: local
  become: yes
  become_user: root
  vars:
    - docroot: /opt/nix.pub/share

使用变量方法:

file:
      path: '{{ docroot }}'

使用Jinja模板,必须是单引号或者双引号

使用Jinja模板,必须是单引号或者双引号

roles(最终使用方法,比playbook更好配置)

roles角色用于组织多个task并封装成完成这些任务需要的数据,但是真实的配置场景里面,我们需要很多变量,文件,模板之类的东西,虽然可以配合playbook使用,但是roles更好,因为它有一个目录结构来规范

roles
  rolename
    - files
    - handlers
    - meta
    - templates
    - tasks
    - vars

我们下面创建一个新的role例子,role的任务有:

  1. Add Nginx Repository- 使用apt_repository模块添加Nginx稳定PPA以获取最新的稳定版本的Nginx 。
  2. Install Nginx - 使用Apt模块安装Nginx。
  3. Create Web Root - 最后创建一个Web根目录。

在每一个子目录中,ansible都会自动找到并读取那个叫main.yml的文件。

创建角色 进入本地ansible-test文件夹,激活虚拟环境:

mkdir nginx  # 这里最好就使用roles这个名字
cd nginx  
ansible-galaxy init nginx

然后进入nginx,会发现整个role目录已经建立好了。

  1. files

里面放置我们需要复制到服务器中的文件

  1. handlers

记得放在main.yml文件中:

- name: Start Nginx
  service:
      name: nginx
      state: started

- name: Reload Nginx
  service:
      name: nginx
      state: reloaded

前面提到了handler需要被task调用才起作用,我们可以在其他的YAML文件中调用它们

  1. meta

这个目录里面的main.yml用于描述角色之间的依赖关系,比如nginx role依赖于ssl role,那么:

dependencies:
    {role: ssl}

这样调用nginx角色时,自动会先调用ssl角色 如果无任何依赖: dependencies: []

  1. Template

这里面不需要有main.yml文件,这里的文件使用的是Jinja2模板引擎的.j2文件 比如我们新建一个文件为conf.j2:

server {
    # Enforce the use of HTTPS
    listen 80 default_server;
    server_name {{ domain }};
    return 301 https://$server_name$request_uri;
}

server {
    listen 443 ssl default_server;

    root /opt/{{ domain }}/share;
    index index.html index.htm ;

    access_log /var/log/nginx/{{ domain }}.log;
    error_log  /var/log/nginx/{{ domain }}-error.log error;

    server_name {{ domain }};

    charset utf-8;

    include h5bp/basic.conf;

    ssl_certificate           {{ ssl_crt }};
    ssl_certificate_key       {{ ssl_key }};
    include h5bp/directive-only/ssl.conf;

    location / {
        try_files $uri $uri/ /index.php$is_args$args;
    }

    location = /favicon.ico { log_not_found off; access_log off; }
    location = /robots.txt  { log_not_found off; access_log off; }

    location ~ \.php$ {
        uwsgi_pass  web;
        include  /etc/nginx/conf/uwsgi_params;
    }

}

这是一个标准的php应用的Nginx配置文件,里面有一些变量

  1. vars

该目录需要有main.yml文件:

domain: serversforhackers.com
ssl_key: /etc/ssl/sfh/sfh.key
ssl_crt: /etc/ssl/sfh/sfh.crt
  1. tasks

这个是最重要的文件夹,使用role的时候,运行的文件就是该文件夹下面的main.yml文件:

- name: Add Nginx Repository
  apt_repository:
      repo: ppa:nginx/stable
      state: present
- name: Install Nginx
apt:
    pkg: nginx
    state: installed
    update_cache: true
notify:
     - Start Nginx

name: Add H5BP Config
copy:
    src: h5bp
    dest: /etc/nginx
    owner: root
    group: root

运行角色 Run Role

我们要对服务器运行一个或者多个role的时候,我们还是需要用到playbook,playbook需要和roles在同一个目录下面,然后创建一个主yml文件,在这个文件里面定义需要使用的角色以及是对哪个服务器执行的操作: 新建 server.yml(这就是那个playbook):

- hosts: local
  connection: local
  roles:
        - nginx

然后老样子运行playbook: ansible-playbook -i ./hosts server.yml

前面几大板块的关系: playbook >> roles >> tasks+handler+… >>modules 总的playbook里面可能就几行,所有各部分的功能交给不同的role来完成。 每个role里面是各种task并且调用handler支撑功能的。 每个task和handler里面的功能是由各种模块modules实现的。

我们使用ssh与托管节点(服务器)通信,默认使用sftp(使用SSH协议进行FTP传输的协议叫做SFTP),如果不可用,需要在ansible.cfg文件中配置成scp方式,这里不建议使用virtualenv虚拟环境来安装ansible。

github地址:https://github.com/ansible/ansible/ 官网地址:https://docs.ansible.com 在线playbook分享平台:https://galaxy.ansible.com

本文链接:http://nix.pub/article/ansible/