ANSIBLE
[TOC]
场景
为了操作远程主机,我们通常是ssh登录到对应的机子上并输入命令执行,安装软件、修改配置、等等。如果只有单台主机,这么做是完全OK的,顶多是把一些常用操作变成脚本减少重复操作。但是当有了两台主机甚至更多主机之后,同样的命令在另一台主机还要敲一遍,这样的操作就有点枯燥繁琐了。有的人觉得仅仅是多一台主机还好,那如果多10台主机呢,比如需求是在10台机子上配置redis集群,那么ssh登录到每台机子上进行操作是一件很sb的事。
一个简单的解决方案是:使用pssh。pssh可以通过ssh的方式批量发送命令到多台主机并执行,pscp可以批量拷贝文件到多台主机。但是pssh的缺点也很明显,就是功能太少。如果想要通过pssh进行复杂的操作,那就比较困难了。
多主机的配置管理是常见的场景,前人已经造好了轮子,也就是一些自动化运维工具,我们直接使用就行了。比较著名的也是国内用的最多的就是Ansible。
其实本质上Ansible或者说大部分运维工具主要解决以下自动化运维场景:
- 文件传输
- 应用部署
- 配置管理
- 任务流编排
常用自动化运维工具
自动化运维工具主要可以分为两大类:
- 需要装代理的;
- 不需要装代理的;
第一种方式是指我们需要预先在目标主机上安装代理软件(在被控端上放个内鬼,就可以交易了),然后主控端通过通知代理软件来操纵被控端。
第二种方式是不需要在被控端安装代理软件,主要是通过ssh连接到被控主机进行控制操作,主控端需要提前得到被控主机的授权(比如秘钥对)才能进行相应的操作。
当前主流的自动化运维工具有:
- ansible:使用python编写,agentless,适用于中小型应用环境;
- saltstack: 使用python编写,需要在每台机子上安装agent,执行效率更高;
- puppet: 使用ruby编写,功能强大,配置复杂,重型,适合大型环境;
- fabric:使用python编写,agentless,功能比较简单;
由于puppet是使用ruby编写的,国内ruby玩家的数量远少于python,因此saltstack和ansible的使用者相对来说就比较多。fabric由于功能太少,完全能被ansible替代,所以企业用户很少使用fabric做复杂的任务流编排。
Ansible
特性
这里简单说一下ansible的主要特性:
- 模块化:调用特定的模块,完成特定的任务,比如说rabbitmq模块特定用于部署rabbitmq,redis模块特定用户部署redis等;
- 有paramiko,pyyaml,jinja2三个关键模块,分别的功能是ssh连接并操作,解析yaml文件,jinjia2模板引擎;
- 支持自定义模块;
- 基于python实现;
- 部署简单,基于python和ssh,agentless;
- 安全,基于openssh;
- 支持playbook编排任务;
- 幂等性,第一次执行某个ansible的操作和第n次执行同样的ansible操作对主机状态的改变结果是一样的;
- 无需代理不依赖pki;
- 可使用任何语言写模块;
- yaml格式,编排任务,支持丰富的数据结构;
- 较强大的多层解决方案;
安装
只要用pip安装即可。
> pip install ansible
或者
> pip3 install ansible
完成。
配置文件主要在:
/etc/ansible/ansible.cfg
,ansible工具本身的配置。/etc/ansible/hosts
,静态主机清单(Inventory)配置。/etc/ansible/roles/
,ansible-roles可以放在这。
组件
Inventory
主机清单。对于批量主机操作,为了便捷的使用其中的部分主机,可以在inventory file中将其分组命名,默认的inventory file为/etc/ansible/hosts
,inventory file可以有多个,且也可以通过Dynamic Inventory来动态生成。inventory文件的格式符合INI
文件风格,中括号中的字符为组名。可以将同一个主机同时归并到多个不同的组中;如果目标主机使用非默认的SSH端口,还可以在主机名称之后使用冒号加端口号来表明。举个例子:
[webserver]
172.1.1.40:2222
10.2.2.2
www.bbb.com
# 主机机名遵循相似的命名模式,还可使用列表的方式标识个主机
[mysqlserver]
db[01:10].dddd.com
[redisserver]
rds-[a:f].cloud.com
# 在inventory中定义主机时为其添加主机变量以便于在playbook中使用
[appserver]
10.3.3.3 maxMemoryUsage=60
10.4.4.4 servicePort=8099
# 定义指定组内所有主机上的在playbook中可用变量
[appsever:vars]
config-center=172.3.4.5
enable-water=true
AD-Hoc
命令行模式使用ansible。
使用ansible --help
可查看如何使用
fwz@fwz-mbp ~/Workspace> ansible --help
Usage: ansible <host-pattern> [options]
Define and run a single task 'playbook' against a set of hosts
Options:
-a MODULE_ARGS, --args=MODULE_ARGS
module arguments
--ask-vault-pass ask for vault password
-B SECONDS, --background=SECONDS
run asynchronously, failing after X seconds
(default=N/A)
-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
......
......
简单的说,语法就是ansible <host-pattern> [-f forks] [-m module_name] [-a args]
本质上,就是调用相应的ansible模块,并传入相应的参数。
举几个例子:
ping所有主机(使用ping模块):
> ansible all -a ping --ask-pass
ansible all -m ping --ask-pass
SSH password:
xx.xx.xx.xxx | SUCCESS => {
"changed": false,
"ping": "pong"
}
改变文件属性(使用file模块):
> ansible mysql-server -m file -a 'owner=mysql group=mysql mode=644 path=/tmp/fstab.ansible'
拷贝文件(使用copy模块):
> ansible storageIndex-server -m copy -a 'src=/home/devops/cloud-relay-1.2.4-SNAPSHOT.tar.gz dest=/root/cloud-storageIndex/cloud-relay-1.2.4.tar.gz'
配置Rabbitmq(使用rabbitmq_user模块):
> ansible rabbitmq-server -m rabbitmq_user -a 'user=new_user password=root tags=administrator vhost=/ state=present'
执行linux的命令(使用shell模块):
> ansible webserver -m shell -a 'netstat -nutlp > netstat_temp.txt'
Playbook
剧本。虽然可以在命令行中使用ansible,但是明显功能有限,无法支持复杂环境的配置管理工作。首先,命令行一次只能执行一条命令,其次,由于通常是手敲命令,因此不好重复。类似于linux中的shell脚本,ansible提供了playbook,翻译为剧本,用于组织基于多个ansible模块交互的工作流编排。
linux的脚本文件是.sh
文件,ansible-playbook则是.yaml
文件,即使用YAML语法。YAML的语法和其他高阶语言类似,并且可以简单表达清单、散列表、标量等数据结构。
直接举个例子:
---
# This playbook will install mysql and create db user and give permissions.
- hosts: $servername
remote_user: root
- name: Install Mysql package
yum: name={{ item }} state=installed
with_items:
- mysql-server
- MySQL-python
- libselinux-python
- libsemanage-python
- name: Configure SELinux to start mysql on any port
seboolean: name=mysql_connect_any state=true persistent=yes
when: sestatus.rc != 0
- name: Create Mysql configuration file
template: src=my.cnf.j2 dest=/etc/my.cnf
notify:
- restart mysql
- name: Start Mysql Service
service: name=mysqld state=started enabled=yes
- name: insert iptables rule
lineinfile: dest=/etc/sysconfig/iptables state=present regexp="{{ mysql_port }}"
insertafter="^:OUTPUT " line="-A INPUT -p tcp --dport {{ mysql_port }} -j ACCEPT"
notify: restart iptables
- name: Create Application Database
mysql_db: name={{ dbname }} state=present
- name: Create Application DB User
mysql_user: name={{ dbuser }} password={{ upassword }} priv=*.*:ALL host='%' state=present
例子很好理解,首先是定义了这个playbook在哪个主机组中执行,以及对应的用户,接着是一组task,分别调用了yum、seboolean、template、service、lineinfile、mysql_db、mysql_user模块。其中使用了notify作handler。
hosts:playbook中的每一个play的目的都是为了让某个或某些主机以某个指定的用户身份执行任务。hosts用于指定要执行指定任务的主机,其可以使一个或多个由冒号分隔主机组;
remote_user: remote_user也可用于各task中,也可以通过指定其通过sudo的方式在远程主机上执行任务,其可用于play全局或其任务;此外,甚至可以在sudo时使用sudo_user指定sudo时切换的用户。
task:play的主题部分是task list。task list中的各任务按次序逐个在hosts中指定的所有主机上执行,即在所有主机上完成第一个任务后再开始第二个。在运行自上而下某playbook时,如果中途发生错误,所有已执行任务都可能回滚,在更正playbook后重新执行一次即可。
taks的目的是使用指定的参数执行模块,而在模块参数中可以使用变量。模块执行是幂等的。这意味着多次执行是安全的,因为其结果均一致。
每个task都应该有其name,用于playbook的执行结果输出,建议其内容尽可能清晰地描述任务执行步骤,如果为提供name,则action的结果将用于输出。
定义task可以使用”action: module options”或”module:options“的格式推荐使用后者以实现向后兼容。如果action一行的内容过多,也中使用在行首使用几个空白字符进行换行。
tasks:
- name:make sure apache is running
service: name=httpd state=started
在众多的模块中,只有command和shell模块仅需要给定一个列表而无需使用”key=value”格式
tasks:
- name: run this command and ignore the result
shell: /usr/bin/somecommand || /bin/true
Handler: 用于当关注的资源发生变化时采取一定的操作。
“notify”这个action可用于在每个play的最后被触发,这样可以避免多次有改变发生时每次都执行执行的操作,取而代之,仅在所有的变化发生完成后一次性地执行指定操作,在notify中列出的操作称为handlers,也即notify中调用handlers中定义的操作。
- name: template configuration file
template: src=template.j2 dest=/etc/foo.conf
notify:
- restart memcached
- restart apache
Roles
ansible自1.2版本引入的新特性,用于层次性、结构化地组织playbook。roles能够根据层次型结构自动转载变量文件、tasks以及handlers等。要使用roles只需要在playbook中使用include指令即可。简单来讲,roles就是通过分别将变量、文件、任务、模板以及处理器放置于单独的目录中,并可以便捷地include他们的一种机制。角色一般用于基于主机构建服务的场景中,但也可以使用于构建守护进程的场景中。
一个常用的role结构:
site.yml
webserver.yml
fooserver.yml
roles/
common/
files/
templates/
tasks/
handlers/
vars/
meta/
webserver/
files/
templates/
tasks/
handlers/
vars/
meta/
在ansible-playbook中可以这么使用role:
- hosts: webserver
roles:
- common
- webserver
向role中传递参数:
- hosts: webserver
roles:
- common
- { role: foo_app_instance, dir:'/opt/a',port:5000}
- { role: foo_app_instance, dir:'/opt/b',port:5001}
- task目录:至少应该包含一个为main.yml的文件,其定义了此角色的任务列表;此文件可以使用include包含其它的位于此目录中的task文件;
- file目录:存放由copy或script等模板块调用的文件;
- template目录:template模块会自动在此目录中寻找jinja2模板文件;
- handlers目录:此目录中应当包含一个main.yml文件,用于定义此角色用到的各handlers,在handler中使用inclnude包含的其它的handlers文件也应该位于此目录中;
- vars目录:应当包含一个main.yml文件,用于定义此角色用到的变量
- meta目录:应当包含一个main.yml文件,用于定义此角色的特殊设定及其依赖关系;ansible1.3及其以后的版本才支持;
- default目录:应当包含一个main.yml文件,用于为当前角色设定默认变量时使用此目录;
官方提供了很多ansible-roles的例子:ansible/ansible-examples
Galaxy
用于分享和下载ansible-role的平台,类似于同性交友网站github。