一、什么是一个好的Ansible Playbook?



干货:什么叫一个好的Ansible Playbook?_java

好的Ansible Playbook的三个原则:

  1. 越简单约好

  2. 可读性越好越好

  3. 通用性越强越好


三个原则看起来有点空,像废话?

我们看一个案例。这个案例是在ec2上部署一套三层应用。


这个三层应用包括:一个前端(haproxy)、两个app(tomcat)、一个db(postgresql)。

干货:什么叫一个好的Ansible Playbook?_java_02


安装这个三层应用,如果手工安装,显然工作量很大,目测至少需要1-2天。但通过ansible,工作量会非常、非常、非常低。前提是,需要写出一个好的playbook。



二、一个不太好的anible playbook

针对安装三层应用的需求,我们先看一个不太好的playbook:

(https://github.com/tonykay/bad-ansible/blob/master/3tier-bad/bad-playbook.yml)


nameconfiguration

hostsall

gather_factsfalse # remove later! speeds up testing

becometrue



tasks:

nameenable repos

template:

src./open_three-tier-app.repo

dest/etc/yum.repos.d/open_three-tier-app.repo

mode0644



namedeploy haproxy

hostsfrontends

gather_factsfalse # remove later! speeds up testing

becometrue



tasks:

namehttp

package:

namehttpie

statelatest

nameinstall HAProxy

yum:

name=haproxy state=latest

nameenable HAProxy

service:

namehaproxy

statestarted

nameconfigure haproxy

template:

src./haproxy.cfg.j2

dest/etc/haproxy/haproxy.cfg

namerestart HAproxy

service:

namehaproxy

staterestarted





namedeploy tomcat

hostsapps

gather_factsfalse

becometrue



tasks:

nameinstall tomcat

package:

nametomcat

statelatest

nameenable tomcat at boot

service:

nametomcat

enabledyes



namecreate ansible tomcat directory

file:

path/usr/share/tomcat/webapps/ROOT

statedirectory



namecopy static index.html to tomcat webapps/ansible/index.html

template:

srcindex.html.j2

dest/usr/share/tomcat/webapps/ROOT/index.html

mode0644



namestart tomcat

service:

nametomcat

statestarted



nameindex.html on app 1

hostsapp1

gather_factsfalse

becometrue



tasks:

namecopy static index.html to tomcat webapps/ansible/index.html

template:

srcindex.html.app1

dest/usr/share/tomcat/webapps/ansible/index.html



nameindex.html on app 1

hostsapp2

gather_factsfalse

becometrue



tasks:

namecopy static index.html to tomcat webapps/ansible/index.html

template:

srcindex.html.app2

dest/usr/share/tomcat/webapps/ansible/index.html



namedeploy postgres

hostsapps

gather_factsfalse

becometrue

hostsappdbs

tasks:

nameinstall progress

command"yum install -y postgresql-server"



#- name: install postgres

#  yum:

#    name: postgresql-server

#    state: latest

nameenable apache at boot

service:

namepostgresql

enabledyes

nametell user to finish setting up postgres

debug:

msg"Either uncomment the postgres setup or manually login and initialize"



# only run the next 2 tasks once!

#  - name: initilize postgres

#    command: postgresql-setup initdb

#  - name: initilize postgres some more

#    command: chkconfig postgresql on

namestart postgres

service:

namepostgresql.service

statestarted



namedeploy apache

hostsapps

gather_factsfalse

becometrue

hostsapps

tasks:

nameinstall apache

yum:

namehttpd

statelatest

nameenable apache at boot

service:

namehttpd

enabledyes

namestart apache

service:

namehttpd

statestarted

Playbook大致做的事情是:

  1. 收集信息(ansible 2.3以后,有这个功能)

  2. enable repos

  3. 安装haproxy、http

  4. 安装tomcat并进行配置 

  5. 安装postgres并进行配置

  6. 安装并启动apache


执行这个playbook以后,通过curl检测前端,部署成功后,通过浏览器进行验证,前端访问的是app1:


front1:

干货:什么叫一个好的Ansible Playbook?_java_03

haproxy的前端对于后端两个应用可以配置负载均衡的策略,当app1出现问题或者负载比较大的时候,前端将访问请求指向app2:

干货:什么叫一个好的Ansible Playbook?_java_04


按理说,能写出这样一个playbook,其实还是很牛的。

但从技术角度,它并不是一个好的playbook。原因:


  1. 这个playbook太长,比较复杂

  2. 这个playbook可读性较差

  3. 这个playbook不方便复用


下来,看一个好的例子。



三、一个好的Ansible Playbook样例


我们看一个好的playbook案例。

(https://github.com/tonykay/good-ansible)


这个playbook的目录结构如下:

干货:什么叫一个好的Ansible Playbook?_java_05


我们看一下主任务:main.yml

---



hostsjumpbox

gather_factsfalse

roles:

{name: osp-facts, when: ansible_product_name ==  'OpenStack Compute'}

# Setup front-end load balancer tier





namesetup load-balancer tier

hostsfrontends

becomeyes

roles:

{name: base-config, tags: base-config}

{name: lb-tier, tags: [lbs, haproxy]}



# Setup application servers tier



namesetup app tier

hostsapps

becomeyes

gather_factsfalse

roles:

{name: base-config, tags: base-config}

{name: app-tier, tags: [apps, tomcat]}



# Setup database tier



namesetup database tier

becomeyes

hostsappdbs

roles:

{name: base-config, tags: base-config}

{name: geerlingguy.postgresql, tags: [dbs, postgres]}

相对于上一个案例,这个main.yml就清爽很多了。无论从可读性、简便性都高很多。


这个yaml做的事情:配置load-balancer tier、app tier、 database tier。然后,这三个大的任务,分别调用写好的roles。


这里,我们仅以配置load-balancer tier举例进行分析。


namesetup load-balancer tier

hostsfrontends

becomeyes

roles:

{name: base-config, tags: base-config}

{name: lb-tier, tags: [lbs, haproxy]}

针对以上内容,配置load-balancer tier的操作会在frontends主机组上执行,用root用户执行配置。


然后,在frontends组的主机上,执行两个role:

  1. 执行base-config role。

  2. 执行lb-tier role 。


接下来,我们看一下这两个role的具体内容。

干货:什么叫一个好的Ansible Playbook?_java_06

查看tasks下的主任务:

---

# Initial, common, system setup steps



nameenable sudo without tty for some ansible commands

replace:

path/etc/sudoers

regexp'^Defaults\s*requiretty'

replace'Defaults  !requiretty'

backupyes



nameenable repos

template:

srcrepos_template.j2

dest/etc/yum.repos.d/open_three-tier-app.repo

mode0644



#- name: setup hostname

#  hostname:

#    name: "{{ inventory_hostname }}"



nameinstall base tools and packages

yum:

name"{{ item }}"

statelatest

with_items:

httpie

python-pip

上边task做的事情:

替换文件内容:将/etc/sudoers文件中的^Defaults\s*requiretty替换为Defaults  !requiretty,

然后,enable repos、设置主机名、安装http和python。


接下来,看一下lb-tier role

干货:什么叫一个好的Ansible Playbook?_java_07

查看主任务:

---



nameinstall {{ payload }}

yum:

name"{{ payload }}"

statelatest



nameenable {{ payload }} at boot

service:

name"{{ payload }}"

enabledyes



nameconfigure haproxy to load balance over app servers

template:

srchaproxy.cfg.j2

dest/etc/haproxy/haproxy.cfg

mode0644



namestart {{ payload }}

service:

name"{{ payload }}"

staterestarted

上面的playbook做的操作:安装、配置、启动一个服务。这个服务是个变量{{ payload }},这个变量会在vars进行设置,通过下面的配置,我们知道这个变量是haproxy。

干货:什么叫一个好的Ansible Playbook?_java_08

所以,本playbook执行的任务就是安装、配置、启动haproxy服务。


那么,将一个大的playbook拆分成role的好处是什么呢?我们看一下main.yml的另外一个任务:

namesetup app tier

hostsapps

becomeyes

gather_factsfalse

roles:

{name: base-config, tags: base-config}

{name: app-tier, tags: [apps, tomcat]}

我们看到,这个任务也是先调用 base-config这个role。


这样,这些roles可以不仅被这个大的playbook调用,也可以方便被其他人调用。


此外,在ansible中,我们可以设置vault,也就是对关键参数进行AES 256加密。然后其他变量可以调用这个加密参数(如密码等机密信息)


我们看一个加密文件:

干货:什么叫一个好的Ansible Playbook?_java_09


那么,我们怎么解密这个文件呢?

在知道密码的情况下(不知道密码我也无法破解),执行如下命令:

干货:什么叫一个好的Ansible Playbook?_java_10

因此,这个加密文件中的内容,key是own_repo_path,value是我抹掉的内容。


而own_repo_path这个参数,在template的文件中被调用:

干货:什么叫一个好的Ansible Playbook?_java_11



书写roles的核心内容modules,是下面的网站上是公开的:

http://docs.ansible.com/ansible/latest/modules/list_of_all_modules.html


而ansible module的迅速壮大,也是ansible目前成为自动化运维领域第一语言的一个重要原因。