一、什么是ansible
我们在管理服务器时首先应该是安装操作系统,而关于自动化安装操作系统工具常见的有:cobbler。而在装完操作系统后就是配置系统的相关服务的配置文件,而关于此类的自动化工具常见的有:puppet、saltstack。而日常维护中我们还需要在各主机执行相关命令等操作,而此类的自动化工具就有:func、fabric已经ansible。最后也是重要的监控系统运行状态的相关自动化工具:cacti、nagios和zabbix等。下面就是将ansible工具的使用。
ansible是个什么东西呢?官方的title是“Ansible is Simple IT Automation”——简单的自动化IT工具。这个工具的目标有这么几项:让我们自动化部署APP;自动化管理配置项;自动化的持续交付;自动化的(AWS)云服务管理。 所有的这几个目标本质上来说都是在一个台或者几台服务器上,执行一系列的命令而已。
ansible的基础架构:
运维工具常见的工作模式
- agent模式: 基于ssl实现。代理工作在被监控端。像puppet。
- agentless模式: 基于ssh服务实现工作在被监控端。监控端是ssh的客户端。
ansible是工作在agentless模式下具有幂等性。ansible在控制端只需要告诉监控端的期望状态就可以实现批量部署。
ansible是基于模块工作的ansible本身没有批量部署的能力。真正具有批量部署的是ansible所运行的模块ansible只是提供一种框架。架构包括:
- 连接插件connection plugins负责和被监控端实现通信。
- Host Inventory:指定操作的主机,是一个配置文件里面定义监控的主机。
- 各种模块核心模块command模块自定义模块 。
- 借助于插件完成记录日志邮件等功能。
- PlayBooks:剧本执行多个任务时。并非必需可以让节点一次性运行多个任务。
二、安装使用ansible
1、安装ansible
# yum install -y ansible
2、安装ansible后几个重要文件说明
/etc/ansible/ansible.cfg :配置文件 /etc/ansible/hosts :Host Inventory文件 /usr/bin/ansible :ansible命令 /usr/bin/ansible-doc :模块相关命令 /usr/bin/ansible-playbook :执行PlayBooks的命令
3、定义Host Inventory
[webservers] node1 node2 [dbservers] node2 node3
说明:各从节点的主机名必须要和上面所定义的一致。
4、定义基于ssh的密钥连接
[[email protected] ~]# ssh-keygen -t rsa -P ‘‘ [[email protected] ~]# ssh-copy-id -i .ssh/id_rsa.pub [email protected] [[email protected] ~]# ssh-copy-id -i .ssh/id_rsa.pub [email protected] [[email protected] ~]# ssh-copy-id -i .ssh/id_rsa.pub [email protected]
语法格式:
ansible <host-pattern> [-f forks] [-m module_name] [-a args]
-m module:默认为command
获取ansible命令所有模块的信息:
[[email protected] ~]# ansible-doc -l
查看指定模块的文档:
[[email protected] ~]# ansible-doc -s 模块
5、基本应用
测试是否能连接个host:
[[email protected] ~]# ansible all -m ping node1 | success >> { "changed": false, "ping": "pong" } node2 | success >> { "changed": false, "ping": "pong" } node3 | success >> { "changed": false, "ping": "pong" }
同步查看个host的时间:
[[email protected] ~]# ansible all -a ‘ntpdate 202.120.2.101‘ [[email protected] ~]# ansible all -m command -a ‘date‘ node3 | success | rc=0 >> Mon Sep 7 11:51:54 CST 2015 node1 | success | rc=0 >> Mon Sep 7 11:51:54 CST 2015 node2 | success | rc=0 >> Mon Sep 7 11:51:54 CST 2015
将Director机器上的cpcom.sh文件拷贝到定义为webservers的机器上的/tmp目录下:
[[email protected] ~]# ansible webservers -m copy -a "src=/root/shell/cpcom.sh dest=/tmp"
验证是否成功:
[[email protected] ~]# ansible webservers -a "ls /tmp/cpcom.sh" node1 | success | rc=0 >> /tmp/cpcom.sh node2 | success | rc=0 >> /tmp/cpcom.sh
将个节点定义个同步时间的周期任务:
[[email protected] ~]# ansible all -m cron -a ‘name="ntpdate" minute=*/5 hour=* day=* month=* job="/usr/sbin/ntpdate 202.120.2.101 &> /dev/null"‘ [[email protected] ~]# ansible all -a "crontab -l" node3 | success | rc=0 >> #Ansible: ntpdate */5 * * * * /usr/sbin/ntpdate 202.120.2.101 &> /dev/null node1 | success | rc=0 >> #Ansible: ntpdate */5 * * * * /usr/sbin/ntpdate 202.120.2.101 &> /dev/null node2 | success | rc=0 >> #Ansible: ntpdate */5 * * * * /usr/sbin/ntpdate 202.120.2.101 &> /dev/null
安装openssl包:
说明,state:选项present|absent,安装或者卸载。name= :安装的包名。
[[email protected] ~]# ansible all -m yum -a "state=present name=openssl"
下面的概念摘自:http://guoting.blog.51cto.com/8886857/1553446
三、YAML语言介绍
1、YAML简介
YAML是一个可读性高的用来表达资料序列的格式。YAML参考了其他多种语言包括XML、C语言、Python、Perl以及电子邮件格式RFC2822等。Clark Evans在2001年在首次发表了这种语言另外Ingy dtNet与Oren Ben-Kiki也是这语言的共同设计者。 YAML Ain‘t Markup Language即YAML不是XML。不过在开发的这种语言时YAML的意思其实是"Yet Another Markup Language"仍是一种标记语言。其特性
- YAML的可读性好
- YAML和脚本语言的交互性好
- YAML使用实现语言的数据类型
- YAML有一个一致的信息模型
- YAML易于实现
- YAML可以基于流来处理
- YAML表达能力强扩展性好
2、YAML语法及playbooks简单应用举例
YAML的语法和其他高阶语言类似,并且可以简单表达清单、散列表、标量等数据结构。其结构(Structure)通过空格来展示,序列(Sequence)里的项用"-"来代表,Map里的键值对用":"分隔。 playbook是由一个或多个“play”组成的列表。play的主要功能在于将事先归并为一组的主机装扮成事先通过ansible中的task定义好的角色。从根本上来讲,所谓task无非是调用ansible的一个module。将多个play组织在一个playbook中,即可以让它们联同起来按事先编排的机制同唱一台大戏。下面是一个简单示例。
- hosts: webservers vars: http_port: 80 max_clients: 256 remote_user: root tasks: - name: ensure apache is at the latest version yum: name=httpd state=present - name: ensure apache is running service: name=httpd state=started handlers: - name: restart apache service: name=httpd state=restarted
YAML文件扩展名通常为.yaml,如example.yaml。
3、playbook基础组件
- Hosts和Users
playbook中的每一个play的目的都是为了让某个或某些主机以某个指定的用户身份执行任务。hosts用于指定要执行指定任务的主机,其可以是一个或多个由冒号分隔主机组;remote_user则用于指定远程主机上的执行任务的用户。如上面示例中的
-hosts: webservers remote_user: root
不过,remote_user也可用于各task中。也可以通过指定其通过sudo的方式在远程主机上执行任务,其可用于play全局或某任务;此外,甚至可以在sudo时使用sudo_user指定sudo时切换的用户。
- hosts: webservers remote_user: test tasks: - name: test connection ping: remote_user: test sudo: yes
- 任务列表和action
play的主体部分是task list。task list中的各任务按次序逐个在hosts中指定的所有主机上执行,即在所有主机上完成第一个任务后再开始第二个。在运行自下而下某playbook时,如果中途发生错误,所有已执行任务都将回滚,因此,在更正playbook后重新执行一次即可。 task的目的是使用指定的参数执行模块,而在模块参数中可以使用变量。模块执行是幂等的,这意味着多次执行是安全的,因为其结果均一致。 每个task都应该有其name,用于playbook的执行结果输出,建议其内容尽可能清晰地描述任务执行步骤。如果未提供name,则action的结果将用于输出。
定义task的可以使用“action: module options”或“module: options”的格式,推荐使用后者以实现向后兼容。如果action一行的内容过多,也中使用在行首使用几个空白字符进行换行。
tasks: - name: make sure apache is running service: name=httpd state=running
在众多模块中,只有command和shell模块仅需要给定一个列表而无需使用“key=value”格式,例如:
tasks: - name: disable selinux command: /sbin/setenforce 0
如果命令或脚本的退出码不为零,可以使用如下方式替代:
tasks: - name: run this command and ignore the result shell: /usr/bin/somecommand || /bin/true
或者使用ignore_errors来忽略错误信息:
tasks: - name: run this command and ignore the result shell: /usr/bin/somecommand ignore_errors: True
- handlers
用于当关注的资源发生变化时采取一定的操作。“notify”这个action可用于在每个play的最后被触发,这样可以避免多次有改变发生时每次都执行指定的操作,取而代之,仅在所有的变化发生完成后一次性地执行指定操作。在notify中列出的操作称为handler,也即notify中调用handler中定义的操作。
- name: template configuration file template: src=template.j2 dest=/etc/foo.conf notify: - restart memcached - restart apache
handler是task列表,这些task与前述的task并没有本质上的不同。
handlers: - name: restart memcached service: name=memcached state=restarted - name: restart apache service: name=apache state=restarted
4、ymal中的变量
- 变量命名 :变量名仅能由字母、数字和下划线组成且只能以字母开头。
- facts :facts是由正在通信的远程目标主机发回的信息这些信息被保存在ansible变量中。要获取指定的远程主机所支持的所有facts可使用如下命令进行
# ansible hostname -m setup 这个命令可以获得被监控端主机的各种信息将这些信息得到后保存到变量中。
- 自定义变量 :在 yaml 中可以使用vars关键字来定义变量
vars:var_name: value
- 变量的引用 :{{ var_name }}
- 特殊的变量迭代: 当有需要重复性执行的任务时可以使用迭代机制。其使用格式为将需要迭代的内容定义为item变量引用并通过with_items语句来指明迭代的元素列表即可。
示例 :例如在被控端添加 2 个用户
方式1一般做法
- name: add user testuser1 user: name=testuser1 state=present groups=wheel - name: add user testuser2 user: name=testuser2 state=present groups=wheel
方式2使用变量方式
- name: add several users vars: user1: testuser1 user2: testuser2 user: name={{ user1 }} state=present groups=wheel user: name={{ user2 }} state=present groups=wheel
方式3使用迭代方式
- name: add several users user: name={{ item }} state=present groups=wheel with_items: - testuser1 - testuser2
事实上with_items中可以使用元素还可为hashes例如
- name: add several users user: name={{ item.name }} state=present groups={{ item.groups }} with_items: - { name: ‘testuser1‘, groups: ‘wheel‘ } - { name: ‘testuser2‘, groups: ‘root‘ }
5、Inentory文件的格式
inventory文件遵循INI文件风格中括号中的字符为组名。可以将同一个主机同时归并到多个不同的组中此外当如若目标主机使用了非默认的SSH端口还可以在主机名称之后使用冒号加端口号来标明。
[webservers] www1.magedu.com:2222 www2.magedu.com [dbservers] db1.magedu.com db2.magedu.com db3.magedu.com
如果主机名称遵循相似的命名模式还可以使用列表的方式标识各主机例如
[webservers] www[01:50].example.com [databases] db-[a:f].example.com
主机变量 :可以在inventory中定义主机时为其添加主机变量以便于在playbook中使用。例如
[webservers] www1.magedu.com http_port=80 maxRequestsPerChild=808 www2.magedu.com http_port=303 maxRequestsPerChild=909
组变量 :组变量是指赋予给指定组内所有主机上的在playbook中可用的变量。例如
[webservers] www1.magedu.com www2.magedu.com [webservers:vars] ntp_server=ntp.magedu.com nfs_server=nfs.magedu.com
组嵌套 :inventory中组还可以包含其它的组并且也可以向组中的主机指定变量。不过这些变量只能在ansible-playbook中使用而ansible不支持。例如
[apache] httpd1.magedu.com httpd2.magedu.com [nginx] ngx1.magedu.com ngx2.magedu.com [webservers:children] apache nginx [webservers:vars] ntp_server=ntp.magedu.com
inventory参数 :ansible基于ssh连接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的循环机制还有更多的高级功能具体请参见官方文档http://docs.ansible.com/playbooks_loops.html。
6、playbooks
playbook是由一个或多个“play”组成的列表。play的主要功能在于将事先归并为一组的主机装扮成事先通过ansible中的task定义好的角色。 从根本上来讲所谓task无非是调用ansible的一个module。将多个play组织在一个playbook中即可以让它们联同起来按事先编排的机制同唱一台大戏。下面列举下playbook基础组件 :
- Hosts和Users
playbook中的每一个play的目的都是为了让某个或某些主机以某个指定的用户身份执行任务。
hosts用于指定要执行指定任务的主机其可以是一个或多个由冒号分隔主机组。
remote_user则用于指定远程主机上的执行任务的用户。
不过remote_user也可用于各task中。也可以通过指定其通过sudo的方式在远程主机上执行任务其可用于play全局或某任务。
此外甚至可以在sudo时使用sudo_user指定sudo时切换的用户。
- hosts: webnodes remote_user: mageedu tasks: - name: test connection ping: remote_user: mageedu sudo: yes
- 任务列表和action
play的主体部分是task list。task list中的各任务按次序逐个在hosts中指定的所有主机上执行即在所有主机上完成第一个任务后再开始第二个。 在运行自下而下某playbook时如果中途发生错误所有已执行任务都将回滚因此在更正playbook后重新执行一次即可。
task的目的是使用指定的参数执行模块而在模块参数中可以使用变量。模块执行是幂等的这意味着多次执行是安全的因为其结果均一致。
每个task都应该有其name用于playbook的执行结果输出建议其内容尽可能清晰地描述任务执行步骤。如果未提供name则action的结果将用于输出。
定义task的可以使用“action: module options”或“module: options”的格式推荐使用后者以实现向后兼容。 如果action一行的内容过多也中使用在行首使用几个空白字符进行换行。
tasks: - name: make sure apache is running service: name=httpd state=running
在众多模块中只有command和shell模块仅需要给定一个列表而无需使用“key=value”格式例如
tasks: - name: disable selinux command: /sbin/setenforce 0如果命令或脚本的退出码不为零可以使用如下方式替代 tasks: - name: run this command and ignore the result shell: /usr/bin/somecommand || /bin/true或者使用ignore_errors来忽略错误信息
tasks: - name: run this command and ignore the result shell: /usr/bin/somecommand ignore_errors: True
- handlers
用于当关注的资源发生变化时采取一定的操作。 “notify”这个action可用于在每个play的最后被触发这样可以避免多次有改变发生时每次都执行指定的操作取而代之仅在所有的变化发生完成后一次性地执行指定操作。在notify中列出的操作称为handler也即notify中调用handler中定义的操作。
- name: template configuration file template: src=template.j2 dest=/etc/foo.conf notify: - restart memcached - restart apache
handler是task列表这些task与前述的task并没有本质上的不同。
handlers: - name: restart memcached service: name=memcached state=restarted - name: restart apache service: name=apache state=restarted
7、tags
tags用于让用户选择运行或路过playbook中的部分代码。ansible具有幂等性因此会自动跳过没有变化的部分即便如此有些代码为测试其确实没有发生变化的时间依然会非常地长。此时如果确信其没有变化就可以通过tags跳过此些代码片断。 示例基于playbooks实现web服务的部署
- 提供好Inventory文件
# /etc/ansible/hosts基于秘钥认证 [webhosts] 172.16.10.22 172.16.10.33
- 编辑 palybooks 剧本
# vim /root/web.yaml - name: web service remote_user: root hosts: webhosts vars: packages: httpd tasks: - name: install httpd yum: name={{ packages }} state=present tags: install - name: configuration httpd copy: src=/root/httpd.conf dest=/etc/httpd/conf/httpd.conf tags: conf notify: - restart httpd - name: service httpd start service: name=httpd enabled=no state=started tags: start - name: add centos and hadoop user user: name={{ item }} state=absent tags: adduser with_items: - centos - hadoop handlers: - name: restart httpd service: name=httpd state=restarted
- 准备好配置文件
将web的配置放到指定目录 src=/root/httpd.conf
- 开始部署
# ansible-playbooks /root/web.yml
yaml脚本应用举例:
[[email protected] ~]# cat httpd.yaml - hosts: all remote_user: root tasks: - name: install httpd yum: state=latest name=httpd - name: httpd configure file copy: src=/root/httpd.conf dest=/etc/httpd/conf/httpd.conf force=yes notify: - restart httpd handlers: - name: restart httpd service: name=httpd state=restarted [[email protected] ~]# ansible-playbook httpd.yaml PLAY [all] ******************************************************************** GATHERING FACTS *************************************************************** ok: [node3] ok: [node2] ok: [node1] TASK: [install httpd] ********************************************************* changed: [node3] changed: [node2] changed: [node1] TASK: [httpd configure file] ************************************************** changed: [node3] changed: [node1] changed: [node2] NOTIFIED: [restart httpd] ***************************************************** changed: [node3] changed: [node1] changed: [node2] PLAY RECAP ******************************************************************** node1 : ok=4 changed=3 unreachable=0 failed=0 node2 : ok=4 changed=3 unreachable=0 failed=0 node3 : ok=4 changed=3 unreachable=0 failed=0 [[email protected] ~]# ansible all -m shell -a "ss -tunl | grep 8080" node3 | success | rc=0 >> tcp LISTEN 0 128 :::8080 :::* node2 | success | rc=0 >> tcp LISTEN 0 128 :::8080 :::* node1 | success | rc=0 >> tcp LISTEN 0 128 :::8080 :::*