ansible:
ansible是新出现的自动化运维工具,基于Python开发,集合了众多运维工具(puppet、cfengine、chef、func、fabric)的优点,实现了批量系统配置、批量程序部署、批量运行命令等功能。ansible是基于模块工作的,本身没有批量部署的能力。真正具有批量部署的是ansible所运行的模块,例如shell,copy,ping等,ansible只是提供一种框架。ansible的主要特性:
模块化:调用特定的模块,完成特定任务
基于Python语言实现,由Paramiko,PyYAML和jinja2三个关键模块
部署简单:agentless,无需在管控主机安装任何客户端
支持自定义模块;
支持playbook(剧本);
运维工具的工作模式:
1、agent :需要在管控主机上安装客户端,和客户端通信,比如:zabbix,puppt
2、agentless:无需安装客户端,基于SSH协议通信,比如:ansible
ansible是工作在agentless模式下具有幂等性,意思就是无论任务执行一遍或者多遍,对结果都不会产生影响。ansible在控制端只需要告诉监控端的期望状态就可以实现批量部署,只需告诉管控主机需要干什么就行。
ansible的架构图:
ansible通过连接插件Connaction Plugins来负责基于SSH和管控主机进行通信,Host Inventoy则记录了需操作主机的信息。ansible除了直接使用命令行控制外,还可以基于PlayBooks(剧本)来实现管理主机,通过各种模块和自定义模块来操作主机,并借助插件来记录日志和邮件等。
一、ansible的安装
]# yum -y install ansible ]# ls /etc/ansible/ ansible.cfg //配置文件 hosts //主机清单 roles/ //定义角色
1、定义hosts:
]# vim /etc/ansible/hosts
[webserver] //定义主机组
172.18.250.77 ansible_ssh_user=root ansible_ssh_pass=123456 //连接主机的账号密码
172.18.250.78 ansible_ssh_user=root ansible_ssh_pass=123456
2、直接给出主机的账号密码不太安全,这时可以基于密钥方式认证:
]# ssh-keygen -t rsa -f ~/.ssh/id_rsa -P'' ]# ansible webserver -m copy -a "src=/root/.ssh/id_rsa.pub dest=/root/" ]# ansible webserver -m shell -a "cat /root/id_rsa.pub >>/root/.ssh/authorized_keys"
3、可以把账号密码去掉,试试能不能执行
]# vim /etc/ansible/hosts [webserver] 172.18.250.77 172.18.250.78 ]# ansible webserver -m ping 172.18.250.78 | success >> { "changed": false, "ping": "pong" } 172.18.250.77 | success >> { "changed": false, "ping": "pong" }
4、ansible的简单语法格式:
ansible HOST-PATTERN [-f forks] [-m MODULE_name] [-a args]
HOST-PATTERN:主机清单,可以是单个主机,也可以是主机组
-m module: 指明模块,默认为command
-f forks: 指明一次发送几个连接请求
-a args: 在远程主机上执行的参数
查看ansible的常用模块命令: ansible-doc -l
5、常用模块
command模块:在远程主机上执行命令,不支持管道
]# ansible webserver -m command -a "cat /etc/issue" 172.18.250.77 | success | rc=0 >> \S Kernel \r on an \m 172.18.250.78 | success | rc=0 >> \S Kernel \r on an \m
shell模块:在远程主机shell上执行命令,支持管道
]# ansible webserver -m shell -a "ss -tan |grep :8080" 172.18.250.78 | success | rc=0 >> LISTEN 0 128 :::8080 :::* 172.18.250.77 | success | rc=0 >> LISTEN 0 128 :::8080 :::*
copy模块:复制本地文件到远程主机,或者直接在远程主机上写内容
1、src=/path/to/somefile dest=/path/to/somefile
2、content=someting dest=/path/to/somefile
]# ansible webserver -m copy -a "src=/etc/issue dest=/tmp" 172.18.250.78 | success >> { "changed": true, "checksum": "5f63a8ace67bf6a8b62aa7da2cf5eccc05a89308", 172.18.250.77 | success >> { "changed": true, "checksum": "5f63a8ace67bf6a8b62aa7da2cf5eccc05a89308", ]# ls /tmp/ issue
cron模块:管理定时计划任务
]# ansible webserver -m cron -a "minute=*/5 state=present job='ntpdate 172.18.0.1 >/dev/null' name=Rsynctime" 172.18.250.78 | success >> { "changed": true, "jobs": [ "Rsynctime" ] } 172.18.250.77 | success >> { "changed": true, "jobs": [ "Rsynctime" ] } ]# crontab -l #Ansible: Rsynctime */5 * * * * ntpdate 172.18.0.1 >/dev/null mintue= //分钟 hour= //小时 day= //哪天 weekday= //星期几 month= //哪月 job= //内容 name= //名称 state= //对内容做什么操作,present:创建 absent:删除
ping模块,对远程主机实行ping操作
]# ansible webserver -m ping 172.18.250.78 | success >> { "changed": false, "ping": "pong" } 172.18.250.77 | success >> { "changed": false, "ping": "pong" }
yum模块,程序包管理
]# ansible webserver -m yum -a "name=httpd state=present" 172.18.250.78 | success >> { "changed": false, "msg": "", "rc": 0, "results": [ "httpd-2.4.6-31.el7.centos.x86_64 providing httpd is already installed" ] } 172.18.250.77 | success >> { "changed": false, "msg": "", "rc": 0, "results": [ "httpd-2.4.6-40.el7.centos.x86_64 providing httpd is already installed" ] }
service模块,管理远程主机上的服务
]# ansible webserver -m service -a "name=httpd state=started enabled=1" 172.18.250.78 | success >> { "changed": true, "enabled": true, "name": "httpd", "state": "started" } 172.18.250.77 | success >> { "changed": true, "enabled": true, "name": "httpd", "state": "started" } name= //程序名称 state= //执行的操作,started、stoped、restarted、reloaded enabled= //是否开机自启 runlevel= //运行的级别
script模块,在远程主机上执行本地脚本,无需传递脚本过去
]# ansible webserver -m script -a "/tmp/mkdir.sh"(本地路径) 172.18.250.78 | success >> { "changed": true, 172.18.250.77 | success >> { "changed": true, 默认是运行在用户的家目录下
user模块和group模块,在远程主机上创建用户和组
]# ansible webserver -m group -a "name=testgroup system=no gid=2000 state=present" ]# ansible webserver -m user -a "name=usertest system=no uid=2000 group=testgroup state=present"
二、playbooks的应用
运行playbook的方式:
1、只检测可能会发生的改变,但不真正执行操作
ansible-playbook --check file.yaml
2、运行
ansible-playbook file.yaml
先要了解YAML的语法,YAML的语法和其他高阶语言类似,并且可以简单表达清单、散列表、标量等数据结构。其结构(Structure)通过空格来展示,序列(Sequence)里的项用"-"来代表,Map里的键值对用":"分隔。
YAML文件扩展名通常为.yaml,如example.yaml。
列表(list)的所有元素均使用“-”打头,例如: # A list of tasty fruits - Apple - Orange 字典(dictionary)通过key与value进行标识,例如: --- # An employee record name: Example Developer job: Developer skill: Elite 也可以将key:value放置于{}中进行表示,例如: --- # An employee record {name: Example Developer, job: Developer, skill: Elite}
playbook是由一个或多个“play”组成的列表。play的主要功能在于将事先归并为一组的主机装扮成事先通过ansible中的task定义好的角色。从根本上来讲,所谓task无非是调用ansible的一个module。将多个play组织在一个playbook中,即可以让它们联同起来按事先编排的机制同唱一台大戏。
playbook的核心元素:
Tasks: 任务
Variables: 变量
Templates: 包含了模板语法的文本文件;
Handlers: 由特定条件触发的任务;
Roles: 角色
Hosts: 主机
1、hosts主机和用户
playbook中的每一个play的目的都是为了让某个或某些主机以某个指定的用户身份执行任务。hosts用于指定要执行指定任务的主机,其可以是一个或多个由冒号分隔主机组;remote_user则用于指定远程主机上的执行任务的用户。
- hosts: webserver remote_user: root tasks: - name: ping ping: remote_user: usertest sudo: yes remote_user也可用于各task中。也可以通过指定其通过sudo的方式在远程主机上执行任务,其可用于play全局或某任务;此外,甚至可以在sudo时使用sudo_user指定sudo时切换的用户。
2、tasks任务和action
play的主体部分是task list。task list中的各任务按次序逐个在hosts中指定的所有主机上执行,即在所有主机上完成第一个任务后再开始第二个。在运行自下而下某playbook时,如果中途发生错误,所有已执行任务都将回滚,因此,在更正playbook后重新执行一次即可。
task的目的是使用指定的参数执行模块,而在模块参数中可以使用变量。模块执行是幂等的,这意味着多次执行是安全的,因为其结果均一致。
每个task都应该有其name,用于playbook的执行结果输出,建议其内容尽可能清晰地描述任务执行步骤。如果未提供name,则action的结果将用于输出。
tasks: - name: yum install httpd packages yum: name=httpd state=present - name: start httpd service: name=httpd state=started
3、 Variables: 变量
3.1、由ansible自己提供的变量:facts,可以直接调用
]# ansible webserver -m setup 172.18.250.78 | success >> { "ansible_facts": { "ansible_all_ipv4_addresses": [ "172.18.250.78" ], "ansible_all_ipv6_addresses": [ "fe80::20c:29ff:fefa:c47b" ], "ansible_architecture": "x86_64", "ansible_bios_date": "07/31/2013", "ansible_bios_version": "6.00",
3.2、ansible-playbook命令的命令行中的自定义变量
]# vim /tmp/test.yaml - hosts: webserver remote_user: root tasks: - name: useradd user: name={{ pkiname }} state=present ]# ansible-playbook -e pkiname=user111 --check test.yaml
3.3 向不同的主机中传递变量,在服务的配置文件中直接调用就行
]# vim /etc/ansible/hosts [webserver] 172.18.250.77 http_port=80 172.18.250.78 http_port=8080
3.4、向组中的主机传递相同的变量,以便在playbook中使用
]# vim /etc/ansible/hosts [webserver] 172.18.250.77 172.18.250.78 [webserver:vars] httpd_port=8080
4、 Templates: 包含了模板语法的文本文件,嵌套有脚本(使用模板编程语言编写)
有时候需要根据服务器的配置来修改服务配置文件,这时可以根据模板来解决
]# vim /tmp/nginx.conf.j2 user nginx; worker_processes {{ ansible_processor_vcpus-1 }}; //根据服务器cpu核数减1 ]# vim /tmp/test.yaml - hosts: webserver remote_user: root tasks: - name: nginx conf template: src=/tmp/template/nginx.conf.j2 dest=/etc/nginx/nginx.conf - name: restart service: name=nginx state=restarted
5、Handlers: 由特定条件触发的任务;
“notify”这个action可用于在每个play的最后被触发,这样可以避免多次有改变发生时每次都执行指定的操作,取而代之,仅在所有的变化发生完成后一次性地执行指定操作。在notify中列出的操作称为handler,也即notify中调用handler中定义的操作。
]# vim /tmp/test.yaml - hosts: webserver remote_user: root tasks: - name: install nginx yum: name=nginx state=present - name: nginx conf template: src=/tmp/template/nginx.conf.j2 dest=/etc/nginx/nginx.conf notify: restart //只有当配置文件发生改变时才会触发notify - name: start nginx service: name=nginx state=started handlers: - name: restart //触发notify时就执行重启操作 service: name=nginx state=restarted
6、playbook还支持条件测试和循环:
条件测试: when语句:在task中使用,jinja2语法格式 tasks: - name: install conf file to centos7 template:src=files/nginx.conf.c7.j2 when: ansible_distribution_major_version == "7" - name: install conf file to centos7 template:src=files/nginx.conf.c6.j2 when: ansible_distribution_major_version == "6" 循环语句:对迭代项的引用,固定变量名为"item",而后,要在task中使用with_items给定要迭代的元素列表; with_items两个方法表示: 列表方法:字符串、字典 1、字符串表示 - hosts: all remote_user: root tasks: - name: install some package yum: name={{ item }} state=present with_items: - nginx - httpd 2、字典表示: - hosts: all remote_user: root tasks: - name: add group group: name={{ item }} state=present with_items: - group11 - group22 - group33 - name: add user user: name={{ item.name }} group={{ item.group }} state=present with_items: - { name: 'user11',group: 'group11' } - { name: 'user22',group: 'group22' } - { name: 'user33',group: 'group33' }
7、tags:标签
tags用于让用户选择运行playbook中的部分代码。ansible具有幂等性,因此会自动跳过没有变化的部分,即便如此,有些代码为测试其确实没有发生变化的时间依然会非常地长。此时,如果确信其没有变化,就可以通过tags跳过此些代码片断。
]# vim /tmp/test.yaml - hosts: webserver remote_user: root tasks: - name: install nginx yum: name=nginx state=present - name: nginx conf template: src=/tmp/template/nginx.conf.j2 dest=/etc/nginx/nginx.conf notify: restart tags: res //在配置文件修改这打标签,执行时不会测试instll和start两部分 - name: start nginx 代码 service: name=nginx state=started handlers: - name: restart service: name=nginx state=restarted ]# ansible-playbook --tags=res --check test.yaml
三、roles
ansilbe自1.2版本引入的新特性,用于层次性、结构化地组织playbook。roles能够根据层次型结构自动装载变量文件、tasks以及handlers等。要使用roles只需要在playbook中使用include指令即可。简单来讲,roles就是通过分别将变量、文件、任务、模板及处理器放置于单独的目录中,并可以便捷地include它们的一种机制。角色一般用于基于主机构建服务的场景中,但也可以是用于构建守护进程等场景中。
创建role的步骤
(1) 创建以roles命名的目录;
(2) 在roles目录中分别创建以各角色名称命名的目录,如webservers等;
(3) 在每个角色命名的目录中分别创建files、handlers、meta、tasks、templates和vars目录;用不到的目录可以创建为空目录,也可以不创建;
(4) 在playbook文件中,调用各角色;
role内各目录中可用的文件
tasks目录:至少应该包含一个名为main.yml的文件,其定义了此角色的任务列表;此文件可以使用include包含其它的位于此目录中的task文件;
files目录:存放由copy或script等模块调用的文件;
templates目录:template模块会自动在此目录中寻找Jinja2模板文件;
handlers目录:此目录中应当包含一个main.yml文件,用于定义此角色用到的各handler;在handler中使用include包含的其它的handler文件也应该位于此目录中;
vars目录:应当包含一个main.yml文件,用于定义此角色用到的变量;
meta目录:应当包含一个main.yml文件,用于定义此角色的特殊设定及其依赖关系;ansible 1.3及其以后的版本才支持;
default目录:为当前角色设定默认变量时使用此目录;应当包含一个main.yml文件;
示例:创建个nginx角色,实现nginx的安装
]# mkdir /etc/ansible/roles/nginx ]# cd /etc/ansible/roles/nginx/ ]# mkdir tasks files templates handlers vars meta default ]# cd tasks/ ]# vim main.yml //编辑task - name: install nginx yum: name=nginx state=present - name: nginx conf template: src=nginx.conf.j2 dest=/etc/nginx/nginx.conf notify: restart nginx tags: res - name: start nginx service: name=nginx state=started ]# cd handlers/ ]# vim main.yml //编辑handlers - name: restart nginx service: name=nginx state=restarted ]# cd templates/ ]# cp /tmp/nginx.conf.j2 ./ //准备模板 ]# vim /etc/ansible/roles/nginx/templates/nginx.conf.j2 user {{ username }}; ]# cd vars/ ]# vim main.yml //定义变量 username: deamon //变量定义时较特殊,不要加“-”,定义运行nginx的用户 ]# vim /tmp/nginx.yml - hosts:websrvs remote_user:root roles: - nginx //直接调用nginx # 调用变量: - host: remote_user: roles: - { role: nginx,username: nginx } # 还可以基于条件测试实现角色调用; - host: remote_user: roles: - { role: nginx,when: ansible_distribution_majro_version == ’7‘ } ]# ansible-playbook --check /tmp/nginx.yml PLAY [webserver] ************************************************************** GATHERING FACTS *************************************************************** ok: [172.18.250.78] ok: [172.18.250.77] TASK: [nginx | install nginx] ************************************************* changed: [172.18.250.78] changed: [172.18.250.77] TASK: [nginx | nginx conf] **************************************************** changed: [172.18.250.78] changed: [172.18.250.77] TASK: [nginx | start nginx] *************************************************** changed: [172.18.250.78] changed: [172.18.250.77] NOTIFIED: [nginx | restart nginx] ********************************************* changed: [172.18.250.78] changed: [172.18.250.77] PLAY RECAP ******************************************************************** 172.18.250.77 : ok=5 changed=4 unreachable=0 failed=0 172.18.250.78 : ok=5 changed=4 unreachable=0 failed=0