ansible是一款自动化运维工具,基于python开发,他是基于各个模块来工作的,主要由以下几个组件:
- connection plugins:用来和需要操作的远程主机通信的,由于ansible是无agent方式,所以需要一种连接方式,一般使用ssh来作为通信方式;
- host inventory:用来指定需要操作的远程主机ip地址,配置文件默认为/etc/ansible/hosts;
- playbook:剧本,就是将事先定义好的需要在哪些远程主机上执行什么操作,写到playbook文件中,然后让ansibles去读取剧本文件并操作;
- core modules:核心模块,ansible是通过模块去到远程主机上执行操作的
- custom modules:自定义模块,除了自带的模块,用户也可以自定义模块
- plugins:一些功能插件,用来实现日志,邮件等功能
结构图如下:
ansible命令
ansible命令是命令行工具,不需要读取playbook文件
ansible :
- -m:指名模块
- -a:给模块指定参数,写在“”里边,如果是命令就直接写进去就行
- -C:测试执行,不真正执行
- -f:一批处理几个,默认为5
- -i:指定hosts文件
- --list-hosts:列出这次操作对哪些主机执行,只是列出
- --syntax-check:检查语法
- -t:在文件中定义tags后使用这个选项只运行tags那一部分
- -c:连接方式 smart为默认,智能选择合适的方式
- -u USERNAME:连接时使用的用户名,默认为none
- -s:sudo
- -S:su
ansible-playbook命令
运行playbook使用ansible-playbook命令,常用参数如下:
- --syntax-check:检测语法
- -C:测试运行,检查错误时候使用,不是真正在目标主机上运行
- --list-hosts:显示要执行的主机
- -t TAGS:使用文件中有标签的地方,只执行标签处的任务
-e VARS:使用变量
模块
ansible是通过模块来工作的,所以下面介绍各种功能的模块:
首先用ansible-doc -l
命令可以列出模块,可以看到有非常多的模块可以使用,这里我们就据介绍一些比较重要的经常用的模块,使用ansible-doc -s 模块名
可以查看模块的参数,用来定义模块的期望值
模块可以定义期望的目标状态,而且操作必须时幂等的(重复数次的结果是相同的,都是定义的期望的状态),如下为一些常用的模块,例子都是使用命令行工具ansible来写的:
1.group模块
ansible all -m group -a "gid=3000 name=mygrp state=present system=no"
state=absent表示删除组,present表示创建组,system=no表示不是系统组
2.user模块
ansible all -m usre -a "uid=5000 name=testuser state=present groups=mygrp shell=/bin/tcsh"
表示用户的uid为5000,用户名为testuser,状态为创建用户,组名为mygrp,使用默认shell为/bin/tcsh
3.copy模块
src选项的值为目录时,最后带/斜杠表示不复制目录本身,不带就表示复制目录本身,src可以为空,用content表示所跟的内容直接生成到目标远程主机文件,不跟dest就源是哪里,目标目录就是哪里,remote_src表示使用远程的源,state=absent表示删除
ansible all -m copy -a "src=/etc/fstab dest=/tmp/test mode=600"
ansible all -m copy -a "content='haha\nhehe\n‘ dest =/tmp/hehe owner=testuser"
4.fetch模块
将远程主机复制到本机,偶尔会用到,不用指定多个主机
command模块
远程主机执行命令:chdir,切换目录;executable,由哪个shell发起执行命令.command命令在执行时不适用shell,所以传递|或者>等参数时不识别,所以要使用shell模块
6.shell模块
解决了command的不识别,其他和command一样
7.file模块
用来创建文件(不推荐,一般用copy,content=空去创建),设定文件属性,path=定义目标文件,state=file|directory
ansible all -m file -a "path=/tmp/hello.dir state=directory"
8.cron模块
指定计划任务,分时日月周和crontab一样,要定义哪个就将哪个写进去
ansible all -m cron -a "minute=*/3 job='/usr/sbin/ntpdate 172.16.0.1 &> /dev/null' name=None "
9.yum模块
name=要安装的包名;state=installd|removed|latest;disable_gpg_check=yes表示禁用gpgcheck
ansible all -m yum -a "name=nginx state=installed disabled_gpg_check=yes"
10.service模块
name=服务名;enabled=yes表示开机启用;runlevel在设置开机启用后指定运行的级别;state=started|stopped|restarted|reloaded;
ansible all -m service -a "name=nginx enabled=true state=started"
11.script模块
本地文件在远程执行,远程自己的脚本用shell模块就行
ansible all -m script -a "脚本路径"
12.setup模块
用来收集对应主机的信息,信息中有很多内建变量可以直接使用,下面讲到变量时候再说这个模块
playbook
命令行方式不能复用,我们可以讲每台主机需要执行的操作写入配置文件playbook,让ansible去读取并执行。
YAML格式:ansible相关文件的格式为YAML,是一种可读性高,用来表达数据序列的格式,基本数据结构为:标量,数组,关联数组.
playbook基本元素:
- hosts:运行指定任务的目标主机
remoute_user:远程主机上执行任务的用户
sudo_user:sudo的用户 - tasks:任务
name:任务名字
module:模块参数
tags:标签,可以使用ansible-playbook 的-t选项来指定,只执行本标签的任务 - handlers:特定条件触发的任务,在一个任务中定义notify,然后这个任务触发之后会通知这个handlers执行操作
还有一些别的元素我们在下面的大标题中挨个讲解,我们先来看这些基础元素的用法:
下面我们来创建一个playbook文件,并执行,注意,一定要缩进:
[root@localhost ansible]# vim /etc/ansible/test.yml
- hosts: 172.16.200.107
remote_user: root
tasks:
- name: install redis
yum: name=redis state=latest
- name: start redis
service: name=redis state=started
然后去检测
[root@localhost ansible]# ansible-playbook --syntax-check test.yml
playbook: test.yml
[root@localhost ansible]# ansible-playbook -C test.yml
PLAY [172.16.200.107] **********************************************************
TASK [setup] *******************************************************************
ok: [172.16.200.107]
TASK [install redis] ***********************************************************
ok: [172.16.200.107]
TASK [start redis] *************************************************************
changed: [172.16.200.107]
PLAY RECAP *********************************************************************
172.16.200.107 : ok=3 changed=1 unreachable=0 failed=0
检测成功后就可以执行了
ansible-playbook test.yml
执行成功的返回结果和测试的一样,所以就不再显示了
变量variables:
变量有四种:
1.内建变量
facter -p:收集本机信息,ansible中有个同样功能的模块,叫做setup,用ansible-doc -s setup查看这个模块
用ansible 172.16.200.107 -m setup 就可以收集目标主机的很多信息了,比facter收集到的信息更多,有很多内建的变量,直接就可以使用了.
使用示例:
[root@localhost ansible]# vim /etc/ansible/test1.yml
- hosts: 172.16.200.107
remote_user: root
tasks:
- name: copy file
copy: content={{ ansible_env }} dest=/tmp/ansible_env #{{}}和变量名字之间要加上空格
[root@localhost ansible]# ansible-playbook --syntax-check test1.yml
[root@localhost ansible]# ansible-playbook -C test1.yml
[root@localhost ansible]# ansible-playbook test1.yml
然后到172.16.200.107的/tmp目录中查看ansible_env文件,看到里边是本机的各种信息,这个ansible_env就是变量
2.自定义变量
先在文件中定义变量
[root@localhost ansible]# vim /etc/ansible/test2.yml
- hosts: 172.16.200.107
remote_user: root
tasks:
- name: install {{ packgs }} #{{}}和变量名字之间要加上空格
yum: name={{ packgs }} state=latest
然后使用的时候调用定义的变量就可以了
[root@localhost ansible]# ansible-playbook --syntax-check test2.yml
[root@localhost ansible]# ansible-playbook -e packgs=httpd -C test2.yml
[root@localhost ansible]# ansible-playbook -e packgs=httpd test2.yml
3.Host Inventory主机文件变量
就是定义在主机文件中的变量,主机文件默认为/etc/ansible/hosts文件,其格式分为两种,变量也有两种,一种可以自定义变量,在模板文件中可以调用,另一种是使用系统本身自带的invertory参数,这个参数是用于定义ansible远程连接目标主机时使用的参数,而非传递给playbook的变量,常用的有以下几种:
ansible_ssh_host
ansible_ssh_port
ansible_ssh_user
ansible_ssh_pass
ansbile_sudo_pass
两种格式:
(a) 单台主机变量
向不同的主机传递不同的变量,在每台主机之后加上变量=值就可以调用了,格式为
IP/HOSTNAME varaiable=value var2=value2
为了验证,首先,我们给172.16.200.107上创建用户feng,密码设置为123
[root@localhost ansible]# ansible 172.16.200.107 -m user -a "name=feng" #注意,这里使用password=密码的选项,设置的密码不是登陆密码,所以还是要使用下一步这种方式
[root@localhost ansible]# ansible 172.16.200.107 -m shell -a "echo 123|passwd --stdin feng"
然后,修改/etc/ansible/hosts文件,这里我们用invertory参数来演示,自定义变量在说到模板时候在用
[web]
172.16.200.107 ansible_ssh_user=feng ansible_ssh_pass=123
172.16.200.108
测试
[root@localhost ansible]# ansible 172.16.200.107 -m ping
172.16.200.107 | SUCCESS => {
"changed": false,
"ping": "pong"
}
(b)组变量
向组中的主机传递相同的变量,就是当组中的每个元素需要设定一个相同的变量时,就可以用这种方式来实现
[groupname:vars]
variable=value
如果107和108两个主机都需要使用feng用户登陆,而密码都是123,那么使用组变量,将之前的设置改一下就可以了,到/etc/ansible/hosts文件中
[web]
172.16.200.107
172.16.200.108
[web:vars]
ansible_ssh_user=feng ansible_ssh_pass=123
这样,组变量就定义好了,组中的每个元素都使用这个定义的变量
4.roles定义变量
在playbook中,加入如下这一段
vars:
- pbvar: playbook var
就可以直接调用了,这种方式主要是在定义角色中使用,详细的在说到角色时候在说明
注意:命令行中调用的变量中间有空格会只显示空格之前的内容
模板templates
ansible-doc -s templates
内嵌一段代码,由模板引擎解析,可以实现变量替换,算数运算等功能,文件中其他内容不变,这种文件叫做模板文件。他们是用模板模块生成的,著名的模板模块叫做python-jinja2,python-jinja2是一个模板引擎,用配置文件后缀为.j2,然后写的时候用python的语法来写.
下面我们创建一个显示本主机ip地址的文件,发给172.16.200.107和108,所以每个主机收到文件内容都是不同的,就实现了模板文件的用法:
root@localhost ansible]# ansible 172.16.200.107 -m setup |less #随便找一台主机获取显示主机ip的变量名为ansible_ens33.ipv4.address
[root@localhost ansible]# vim /etc/ansible/ip.j2 #定义模板文件,加入如下内容
ipaddress: {{ ansible_ens33.ipv4.address }}
[root@localhost ansible]# vim /etc/ansible/test3.yml #写playbook文件,注意template模板不能在命令行中被调用
- hosts: all
remote_user: root
tasks:
- name: template test
template: src=/etc/ansible/ip.j2 dest=/tmp/
[root@localhost ansible]# ansible-playbook -C test3.yml
[root@localhost ansible]# ansible-playbook test3.yml
然后到107和108中查看/tmp/ip.j2文件,发现分别为172.16.200.107和172.16.200.108,证明模板引擎已经将变量解析为本机ip了。
条件测试when
可以在tasks中使用,判定条件满足时候才会执行当前任务,我们用版本号举例,当107的版本为7时,就执行写一个centos7字符串到107主机的/tmp/versions文件中,6的话就写6
[root@localhost ansible]# vim test4.yml
- hosts: 172.16.200.107
remote_user: root
tasks:
- name: test when
copy: content="centos7" dest=/tmp/versions
when: ansible_distribution_major_version == "7"
- name: test when
copy: content="centos6" dest=/tmp/versions
when: ansible_distribution_major_version == "6"
[root@localhost ansible]# ansible-playbook test4.yml #执行
PLAY [172.16.200.107] **********************************************************
TASK [setup] *******************************************************************
ok: [172.16.200.107]
TASK [test when] ***************************************************************
changed: [172.16.200.107]
TASK [test when] ***************************************************************
skipping: [172.16.200.107]
PLAY RECAP *********************************************************************
172.16.200.107 : ok=2 changed=1 unreachable=0 failed=0
我们看到第二个任务skip跳过了,就是因为107是centos7的系统,当检测不是6的时候,第二个任务就跳过了,我们也可以在107中查看/tmp/versions文件,显示的为"centos7"
循环迭代with_items
当有很多需要重复执行而任务内容不变的参数时候,重复的写任务会很麻烦,这个时候需要用到循环,更加高效的来写参数不同,但是任务过程相同的任务
[root@localhost ansible]# vim /etc/ansible/test5.yml
- hosts: 172.16.200.107
remote_user: root
tasks:
- name: install {{ item }} packges
yum: name={{ item }} state=latest
with_items:
- nginx
- varnish
[root@localhost ansible]# ansible-playbook test5.yml
完成后,我们会看到nginx和varnish都被安装上了
也可以用字典来迭代:
user: name={{item.name}} group={{item.group}}
with_items:
- {name: 'feng',group:'fengkp'}
- ...
这里就不在列出字典的用法了。
角色
自包含的目录结构,就是在一个目录中放入部署这一套服务的各种需要的组件.说白了就是将playbook中的各项组件拆分在各个子文件夹中,由一个主目录将所有内容包含在其中,这样做的好处是条理清楚,修改时候比较方便。
目录结构如下:
- files/ :存放由copy或script模块等调用的文件;
- templates/:template模块查找所需要模板文件的目录;
- tasks/:至少应该包含一个名为main.yml的文件;其它的文件需要在此文件中通过include进行包含;
- handlers/:至少应该包含一个名为main.yml的文件;其它的文件需要在此文件中通过include进行包含;
- vars/:至少应该包含一个名为main.yml的文件;其它的文件需要在此文件中通过include进行包含;
- meta/:至少应该包含一个名为main.yml的文件,定义当前角色的特殊设定及其依赖关系;其它的文件需要在此文件中通过include进行包含;
- default/:设定默认变量时使用此目录中的main.yml文件;
下面我们来创建一个nginx角色:
mkdir -pv /etc/ansible/roles/nginx/{files,templates,tasks,vars,handlers,meta,default}
[root@localhost ansible]# cd /etc/ansible/roles/nginx/
[root@localhost nginx]# vim tasks/main.yml
- name: install nginx
yum: name=nginx state=latest
when: ansible_os_family == "RedHat"
有一个任务,我们就可以使用了,使用的话在playbook中调用需要使用roles 加上角色名就可以:
[root@localhost nginx]# vim /etc/ansible/nginx.yml
- hosts: 172.16.200.107
remote_user: root
roles:
- nginx
测试
[root@localhost ansible]# ansible-playbook nginx.yml
当然,这里只使用了tasks元素,如果由其他元素的话,就写在相应的目录下的main.yml文件中就可以,注意写脚本的main.yml的文件时候,由于脚本没有键值,所以不是list,不用加前边的-符号。下面我们通过一个大实验来熟练ansible的操作
实验
通过ansible来实现一个架构,架构图如下:
环境:
- 各个节点时间同步,selinux和iptables关闭
- ansible服务器:172.16.200.109
- 172.16.200.102:部署nginx反代调度器1,部署keepalived,部署varnish服务1
- 172.16.200.103:部署nginx反代调度器2,部署keepalived,部署varnish服务2
- vip:172.16.200.200
- 172.16.200.104:后端nginx节点1,tomcat节点1,mysql主节点,redis主节点
- 172.16.200.105:后端nginx节点2,tomcat节点2,mysql从节点和redis从节点
实现步骤:
我们将结构拆分为静态和动态两条,静态的结构如下:
先来实现静态这条路线上的nginx,tomcat和redis
1.几台主机之间设置ssh密钥互信
在ansible服务端172.16.200.109执行
[root@localhost ansible]# ssh-keygen -t rsa -P ""
[root@localhost ansible]# for i in {2..5};do ssh-copy-id 172.16.200.10$i;done
分别用ssh登陆上去测试是否已经互信,成功后进入下一步
2.在ansible主机设置/etc/ansible/hosts文件,将主机分组
[director]
172.16.200.10[2:3]
[node]
172.16.200.10[4:5]
3.实现nginx角色,并安装
在ansible主机上
设置nginx配置文件
[root@localhost ansible]# mkdir -p /etc/ansible/roles/nginx/{files,templates,tasks,vars,handlers,meta,default}
[root@localhost ansible]# cd /etc/ansible/roles/nginx/
[root@localhost nginx]# vim files/nginx_ansible.conf
upstream varnish {
server 172.16.200.102:6081;
server 172.16.200.103:6081;
}
upstream tomcat {
server 172.16.200.104:8080;
server 172.16.200.105:8080;
}
server {
listen 80;
server_name www.feng.com;
location ~ .*\.(js|css|htm|html|gif|jpg|jpeg|png|bmp|swf|ioc|rar|zip|txt|flv|mid|doc|ppt|pdf|xls|mp3|wma)$ {
proxy_pass http://varnish;
}
location ~ \.(jsp|jspx|do|action)(\/.*)?$ {
proxy_pass http://tomcat;
}
}
设置nginx任务文件
[root@localhost nginx]# vim tasks/main.yml
- name: install nginx
yum: name=nginx state=latest
when: ansible_os_family == "RedHat"
- name: delete default.conf
shell: rm -f /etc/nginx/conf.d/default.conf
- name: install nginx config
copy: src=nginx_ansible.conf dest=/etc/nginx/conf.d/
- name: start nginx
service: name=nginx state=started
设置执行nginx的playbook文件
[root@localhost nginx]# vim nginx.yml
- hosts: director
remote_user: root
roles:
- nginx
然后执行安装
[root@localhost nginx]# ansible-playbook --syntax-check nginx.yml
[root@localhost nginx]# ansible-playbook nginx.yml
PLAY [director] ****************************************************************
TASK [setup] *******************************************************************
ok: [172.16.200.102]
ok: [172.16.200.103]
TASK [nginx : install nginx] ***************************************************
changed: [172.16.200.103]
changed: [172.16.200.102]
TASK [nginx : delete default.conf] *********************************************
changed: [172.16.200.103]
[WARNING]: Consider using file module with state=absent rather than running rm
changed: [172.16.200.102]
TASK [nginx : install nginx config] ********************************************
changed: [172.16.200.103]
changed: [172.16.200.102]
TASK [nginx : start nginx] *****************************************************
changed: [172.16.200.103]
changed: [172.16.200.102]
PLAY RECAP *********************************************************************
172.16.200.102 : ok=5 changed=4 unreachable=0 failed=0
172.16.200.103 : ok=5 changed=4 unreachable=0 failed=0
第一步成功
4.实现tomcat角色并安装
创建目录结构并设置任务文件
[root@localhost nginx]# mkdir -p /etc/ansible/roles/tomcat/{files,templates,tasks,vars,handlers,meta,default}
[root@localhost nginx]# cd /etc/ansible/roles/tomcat/
[root@localhost tomcat]# vim tasks/main.yml
- name: install openjdk
yum: name=java-1.8.0-openjdk-devel state=latest
when: ansible_os_family == "RedHat"
- name: install tomcat
yum: name={{ item }} state=latest
with_items:
- tomcat-lib
- tomcat-admin-webapps
- tomcat-docs-webapp
- tomcat-webapps
- name: start tomcat
service: name=tomcat state=started
设置tomcat的playbook文件
[root@localhost tomcat]# vim tomcat.yml
- hosts: node
remote_user: root
roles:
- tomcat
执行安装
[root@localhost tomcat]# ansible-playbook tomcat.yml
PLAY [node] ********************************************************************
TASK [setup] *******************************************************************
ok: [172.16.200.105]
ok: [172.16.200.104]
TASK [tomcat : install openjdk] ************************************************
changed: [172.16.200.104]
changed: [172.16.200.105]
TASK [tomcat : install tomcat] *************************************************
changed: [172.16.200.105] => (item=[u'tomcat-lib', u'tomcat-admin-webapps', u'tomcat-docs-webapp', u'tomcat-webapps'])
changed: [172.16.200.104] => (item=[u'tomcat-lib', u'tomcat-admin-webapps', u'tomcat-docs-webapp', u'tomcat-webapps'])
TASK [tomcat : start tomcat] ***************************************************
changed: [172.16.200.104]
changed: [172.16.200.105]
PLAY RECAP *********************************************************************
172.16.200.104 : ok=4 changed=3 unreachable=0 failed=0
172.16.200.105 : ok=4 changed=3 unreachable=0 failed=0
成功,可以接着下一步了
5.动态的安装完成后安装静态这条线
首先要安在两个调度器节点上安装varnish
创建目录并设置varnish配置文件
[root@localhost tomcat]# mkdir -p /etc/ansible/roles/varnish/{files,templates,tasks,vars,handlers,meta,default}
[root@localhost tomcat]# cd /etc/ansible/roles/varnish/
[root@localhost varnish]# vim files/default.vcl
import directors;
backend web1 {
.host="172.16.200.104";
.port="80";
}
backend web2 {
.host="172.16.200.105";
.port="80";
}
sub vcl_init {
new server = directors.round_robin();
server.add_backend(web1);
server.add_backend(web2);
}
sub vcl_recv {
if (req.method == "PURGE") {
return(purge);
}
set req.backend_hint = server.backend();
}
sub vcl_pipe {
return (pipe);
}
sub vcl_pass {
return (fetch);
}
设置任务文件
[root@localhost varnish]# vim tasks/main.yml
- name: install varnish
yum: name=varnish state=latest
when: ansible_os_family == "RedHat"
- name: install varnish config
copy: src=default.vcl dest=/etc/varnish
- name: start varnish
service: name=varnish state=started
设置varnish的playbook
- hosts: director
remote_user: root
roles:
- varnish