您的位置:首页 > 其它

第4章 Playbook快速入门

2017-12-10 16:46 351 查看
     Ansible使用YAML语法描述配置文件

     Ansible的任务配置文件被称为Playbook,我们可以称之为"剧本"。

4.1 Playbook语法简介

    Playbook采用YAML语法编写,YAML不是一种标记语言。该语言在被开发时,它的意思其实是:Yet Another Markup Language(仍是一种标记语言)。

4.1.1 多行缩进

   数据结构可以用类似大纲的缩排方式呈现,结构通过缩进来表示,连续的项目通过减号"-"来表示,map结构里面的key/value对用冒号":"来分隔。格式如以下代码所示:

house:
family:
name:Doe
parents:
- John
- Jane
children:
- Paul
- Mark
- Simone
address:
number:34
street:Main Street
city:Nowheretown
zipcode:12345


注意:

1)字串不一定要用双引号标识;

2)在缩排中空白字符的数目并不重要,只要相同阶层的元素左侧对齐就可以了(不过不能使用Tab字符);

3)允许在文件中加入选择性的空行,以增加可读性;

4)选择性的符号"..."可以用来表示档案结尾(在利用串流的通信中,这非常有用,可以在不关闭串流的情况下,发送结束信号)。

4.1.2 单行缩写

      YAML也有用来描述好几行相同结构的数据的缩写语法,数组用"[]"包括起来,hash用"{}"来包括。因此,上面的这个YAML能够缩写成这样:

house:
family:{name:Doe,parents:[John,Jane],children:[Paul,Mark,Simone]
}
address:{number:34,street:Main Street,city:Nowheretown,zipcode:12345
}


     了解了普通的YAML格式文件,我们来看一下正式的Playbook内容是什么样的。Playbook代码如下:

---
# 这个是你选择的主机
- hosts:webservers
# 这个是变量
vars:
http_prot:80
max_clients:200
# 远端的执行权限
remote_user:root
tasks:
# 利用YUM模块来操作
- name:ensure apache is at the latest version
yum:pkg=httpd state=latest
- name:write the apache config file
template:src=/srv/httpd.j2 dest=/etc/httpd.conf
# 触发重启服务器
notify:restart apache
- name:ensure apache is running
service:name=httpd state=started
# 这里的restart apache和上面的触发是配对的。这就是handlers的作用。相当于tag
handlers:
- name:restart apache
service:name=httpd state=restarted


总的来说,Playbook语法具有如下一些特性。

需要以"---"(3个减号)开始,且需顶行首写。
此行开始正常写Playbook的内容,建议写明该Playbook的功能。
使用#号注释代码。
缩进必须是统一的,不能将空格和Tab混用。
缩进的级别必须是一致的,同样的缩进代表同样的级别,程序判别配置的级别是通过缩进结合换行来实现的。
YAML文件内容和Linux系统大小写判断方式保持一致,是区别大小写的,k/v的值均需大小写敏感。
k/v的值可同行写也可换行写。同行使用":"分隔,换行写需要以"-"分隔。
一个完整的代码块功能需最少元素,需包括name:task。
一个name只能包括一个task。

4.2 Playbook案例分析

Shell脚本与Playbook的转换

下面一段代码是一个安装Apache的Shell脚本。

# !/bin/bash
# 安装Apache
yum install --quiet -y httpd httpd-devel
# 复制配置文件
cp /path/to/config/httpd.conf /etc/httpd/conf/httpd.conf
cp /path/to/httpd-vhosts.conf /etc/httpd/conf/httpd-vhosts.conf
# 启动Apache,并设置开机启动
service httpd start
chkconfig httpd on


将上述Shell脚本转换为一个完整的Playbook后,内容如以下代码所示:

---
- hosts:all
tasks:
- name:"安装Apache"
command:yum install --quiet -y httpd httpd-devel
- name:"复制配置文件"
command:cp /tmp/httpd.conf /etc/httpd/conf/httpd.conf
command:cp /tmp/httpd-vhosts.conf /etc/httpd/conf/httpd-vhosts.conf
- name:"启动Apache,并设置开机启动"
command:service httpd start
command:chkconfig httpd on
将以上内容放在一个名为playbook.yml的文件中,直接调用ansible-playbook命令,即可运行,运行结果和脚本运行结果一致。

ansible-playbook ./playbook.yml

部署Apache服务

---
- hosts:all
sudo:yes
tasks:
- name:安装Apache
yum:name={{ item }} state=present
with_items:
- httpd
- httpd-devel
- name:复制配置文件
copy:
src:"{{ item.src }}"
dest:"{{ item.dest }}"
owner:root
group:root
mode:0644
with_items:
- {
src:"/tmp/httpd.conf",
dest:"/etc/httpd/conf/httpd.conf" }
- {
src:"/tmp/httpd-vhosts.conf",
dest:"/etc/httpd/conf/httpd-vhosts.conf" }
- name:检查Apache运行状态,并设置开机启动
service:name=httpd state=started enabled=yes


    本例中,我们使用了Ansible的yum模块、copy模块以及service模块,来完成对Apache服务的安装、配置和运行状态维护等一系列操作。

    第1行,"---",这个是YAML语法中注释的用法,就像shell脚本中的"#"号一样。

    第2行,"- hosts:all",告诉Ansible具体要在那些主机上运行我的Playbook,在本例中是all,即所有主机。

    第3行,"sudo:yes",告诉Ansible通过sudo来运行相应命令,这样所有命令将会以root身份执行。

    第4行,"tasks:",指定一系列将要运行的任务。

    每一个任务以"- name:安装Apache"开头。"- name:"字段并不是一个模块,不会执行任务实质性的操作,他只是给"task"一个易于识别的名称。即便把name字段对应的行完全删除,也不会有任何问题。

    本例中我们使用了yum模块来安装Apache,替代了"yum -y install httpd httpd-devel"。

    在每一个play当中,都可以使用with_items来定义变量,并通过"{{变量名}}"的形式来直接使用。我们使用yum模块的state=present选项来确保软件被安装,或者使用state=absent来确保软件被删除。

    第二个任务同样是"- name"字符开头。我们使用copy模块来将"src"定义的源文件(必须是Ansible所在服务器上的本地文件)复制到"dest"定义的目的地址(此地址为远程主机上的地址)去。在传递文件的同时,还定义了文件的属主,属组和权限。在这个play中,我们用数组的形式给变量赋值,使用{var1:value,var2:value}的格式来赋值,变量的个数可以任意多,不同变量间以逗号分隔,使用{{item.var1}}的形式来调用变量,本例中为{{item.src}}。

    第三个任务使用了同样的结构,调用了service模块,以保证服务的正常开启。

4.3 Playbook与Shell脚本差异对比

    Ansible自带幂等判断机制也为运维省去不少伤脑筋费心的人脑逻辑判断运算。当我们重复执行一个Playbook时,当Ansible发现系统的现有状态与Playbook所定义的将要实现的状态一致时,Ansible将自动跳过该操作。

    我们再次执行Playbook:temp.yml,当Ansible发现Playbook中的play都已完成时,它将直接返回ok状态码,速度非常之快。如果是Shell脚本,肯定会把所用操作再做一遍。

    在正式运行Playbook之前,可以使用--check或-C选项来检测Playbook都会改变那些内容,显示的结果跟真正执行时一模一样,但不会真的对被管理的服务器产生实际影响。

4.4 Ansible-playbook实战小技巧

4.4.1 限定执行范围

当playbook指定的一批主机中有个别主机需进行变更时,我们不需要去修改Playbook文件本身,而是通过一些选项就可以直接限定和查看Ansible命令的执行范围。

1.--limit

我们可以通过修改"- hosts:"字段来指定那些主机将会应用Playbook的操作。

指定一台主机:www.magedu.com
指定多台主机:www.magedu.com,www.osstep.com
指定一组主机:dbserver
当然,也可以直接通过Ansible-playbook命令来指定主机。

# ansible-playbook playbook.yml --limit webservers


这样一来(假设你的inventory文件中包含webservers组,即便Playbook中设定"hosts:all",但也仅对webservers组生效。

2.--list-hosts

执行Playbook时,那些主机会受到影响,使用--list-hosts选项。

ansible-playbook playbook.yml --list-hosts


4.4.2 用户于权限设置

(1)--remote-user

    在Playbook中,如果在hosts字段下没有定义users关键字,那么Ansible将会使用你在Inventory文件中的定义的用户,如果Inventory文件中也没定义用户,Ansible将默认使用当前系统用户身份来通过SSH连接远程主机,在远程主机中运行Play内容。

    我们也可以直接在ansible-playbook中使用--remote-user选项来指定用户。

#ansible-playbook playbook.yml --remote-user=tom


(2)--ask-sudo-pass

    在某些情况下,我们需要传递sudo密码到远程主机,来保证sudo命令的正常运行。这时,可以使用--ask-sudo-pass(-K)选项来交互式的输入密码。

(3)--sudo

    使用--sudo选项,可以强制所有play都使用sudo用户,同时使用--sudo-user选项指定sudo可以执行那个用户的权限,如果不指定,则默认以root身份运行。

比如,当前用户Tom想以Jerry的身份运行Playbook,命令如下:

#ansible-playbook playbook.yml --sudo --sudo-user=jerry --ask-sudo-pass


4.4.3 Ansible-playbook:其他选项技巧

--inventory=PATH(-i PATH):指定inventory文件,默认文件是/etc/ansible/hosts。
--verbose(-v):显示详细输出,也可以使用-vvvv显示精确到每分钟的输出。
--extra-vars=VARS(-e VARS):定义在Playbook使用的变量,格式为:"key=value,key=value"。
--forks=NUM(-f NUM):指定并发执行的任务数,默认为5,根据服务器性能,调大这个值可提高Ansible执行效率。
--connection=TYPE(-c TYPE):指定连接远程主机的方式,默认为SSH,设为local时,则只在本地执行Playbook,建议不做修改。、
--check:检测模式,Playbook中定义的所有任务将在每台远程主机上进行检测,但并不真正执行。

4.5 实战一:Ansible部署Node.js企业实战



4.5.1 添加第三方源

# !/bin/bash
# 导入Remi GPG密钥
wget http://rpms.famillecollet.com/RPM-GPG-KEY-remi -O /etc/pki/rpm-gpg/RPM-GPG-KEY-remi

# 安装Remi源
rpm -Uvh --quiet http://rpms.famillecollet.com/enterprise/remi-release-6.rpm 
# 安装EPEL源
yum install epel-release

# 安装node.js(npm+和它的依赖关系)
yum --enablerepo=epel install npm


使用ansible编写:

---
- hosts: proxy

vars:
# Vars can also be passed in via CLI with `--extra-vars="name=value"`.
node_apps_location: /usr/local/opt/node

tasks:
- name: Install Remi repo.
yum:
name: "http://rpms.remirepo.net/enterprise/remi-release-6.rpm"
state: present

- name: Import Remi GPG key.
rpm_key:
key: "http://rpms.remirepo.net/RPM-GPG-KEY-remi"
state: present

- name: Install EPEL repo.
yum: name=epel-release state=present

- name: Ensure firewalld is stopped (since this is a test server).
service: name=iptables state=stopped

- name: Install Node.js and npm.
yum: name=npm state=present enablerepo=epel

- name: Use Taobao npm repo
command: >
npm config set registry https://registry.npm.taobao.org 
- name: Disable https of npm
command: >
npm config set strict-ssl false

- name: Install Forever (to run our Node.js app).
npm: name=forever global=yes state=present


rpm_key是一个Ansible模块,用于从你的RPM数据库中添加或移除GPG key。我们正在从Remi的源中导入一个可以。

   Node.js应用部署。首先,通过创建一个新的名为app的文件夹,这个文件夹和我们上一不创建的ymal文件处于相同的路径下面。然后在这个文件夹里面创建文件app.js,并编辑内容如下:

// Load the express module.
var express = require('express'),
app = express.createServer();

// Respond to requests for / with 'Hello World'.
app.get('/', function(req, res){
res.send('<h1>Hello World!</h1>');
});

// Listen on port 80 (like a true web server).
app.listen(80);
console.log('Express server started successfully.');


     因为这个小app依赖于Express(一个简单的Node的HTTP框架),我们还需要通过一个package.json文件告诉NPM关于它的依赖关系,这个文件域app.js处于相同的路径下面。

{
"name": "examplenodeapp",
"description": "Example Express Node.js app.",
"author": "Jeff Geerling <geerlingguy@mac.com>",
"dependencies": {
"express": "3.x.x"
},
"engine": "node >= 0.10.6"
}


   然后添加下面内容到你的Playbook里面,这段代码将复制整个app目录到目标服务器,然后使用npm下载安装所依赖的文件(这里为express)

- name: Ensure Node.js app folder exists.
file: "path={{ node_apps_location }} state=directory"

- name: Copy example Node.js app to server.
copy: "src=app dest={{ node_apps_location }}"

- name: Install app dependencies defined in package.json.
npm: "path={{ node_apps_location }}/app"


     首先我们使用file模块确保我们安装的app目录存在。{{node_apps_location}}变量可以在vars部分定义,vars部分位于playbook的顶部。当然也可以在Inventory文件中定义,也可以在运行ansible-playbook的时候定义。

     我们使用Ansible的copy模块复制整个app目录到测试服务器,copy模块可以自动区分单一的文件的包含文件的目录,然后在目录中递归,就像rsync或scp。

     最后,我们使用npm模块,这次除了app的路径之外没有额外的参数。这告诉NPM来解析package.json文件,然后确保所有的依赖关系都存在。

4.5.2 运行Node.js进程

     我们现在使用forever命令来启动这个app。

- name: Check list of running Node.js apps.
command: forever list
register: forever_list
changed_when: false

- name: Start example Node.js app.
command: "forever start {{ node_apps_location }}/app/app.js"
when: "forever_list.stdout.find('{{ node_apps_location }}/app/app.js') == -1"


     在第1个任务中,我们做了两件新的事情。

1)register创建了一个新的变量forever_list,以便于下次任务的时候作判断条件。register用于保存命令的输出(包括标准输出和错误输出),然后赋给变量名。

2)changed_when可以在任务运行完成后,明确告诉Ansible这个任务是否对主机造成了影响(比如改变了文件或安装了软件等)。在本次任务中,forever list命令永远都不会导致服务器的改变,所以我们指定其值为false。

    第2个任务实际上使用forever启动了这个app。我们也可以通过调用node {{node_apps_location}}/app/app.js启动这个app,不过这种方式更难控制。

    forever会一直跟踪它所管理的Node app,然后我们使用forever的list选项打印正在运行的app列表。我们第一次运行这个Playbook时候,因为之前从未启动过Node.js app,所以这个列表会是空的。我们使用when语句来判断app的路径是否在forever list的输出信息中,如果不存在,则表明我们的Node.js app还未启动,于是触发任务启动之。

4.5.3 Node.js app服务部署总结

   到这个时候,我们已将完成了可以安装一个简单的可以通过80端口相应的HTTP请求的Node.js app。

   通过如下命令使我们编辑的Playbook在服务器上生效,同时使用--extra-vars选项来对代码中的变量node_apps_location进行赋值 。

ansible-playbook --extra-vars="node_apps_location=/usr/local/opt/node"








4.6 实战二:Drupal基于LAMP的自动化部署

    Drupal是使用PHP语言编写的开源内容管理框架(CMF),它由内容管理系统(CMS)和PHP开发框架(Framework)共同构成。他连续多年荣获全球最佳CMS大奖,是基于PHP语言最著名的Web应用程序。



4.6.1 定义变量并设置Handlers

    在Playbook中使用变量文件来定义变量,这样可以使Playbook看起来更加整洁,同时也能提高效率。我们首先使用下面的语法在Playbook文件playbook.yml头部添加对变量文件的引用。

---
- hosts: proxy
vars_files:
- vars.yml


    变量文件可以实现变量的集中管理,使得变量的管理更加方便、高效。

    Ansible中,我们可以在Playbook中使用prea_task和post_task指定在主任务运行之前和之后要运行的任务。在本例中,需要确保在我们正式开始运行部署LAMP的任务之前,APT缓存是被更新过的,也就是在任务开始前我们主机上的软件包都必须是最新的。这时,我们将得用apt模块来更新APT缓存,同时设置缓存有效期为3600秒。实现方法如下:

pre_tasks:
- name: Update apt cache if needed.
apt: update_cache=yes cache_valid_time=3600


    解决了APT缓存的问题之后,我们将借助handlers来实现对apache2服务的启动管理。

handlers:
- name: restart apache
service: name=apache2 state=restarted
    Handlers是Playbook中一种特殊的任务类型,我们通过在任务末尾使用notify选项加Handlers的名称,来触发对应名称下Handlers中定义的任务。在本例中,我们将在Apache配置完成后,或Apache配置文件变动过后,使用notify:restart apache来调用handler重启Apache服务。

    默认情况下,当一个Playbook中的任务执行失败时,Ansible会停止所有的任务,而且也不会在触发任何本该被触发的Handlers。在某些情况下,这种默认机制会造成不可预测的负面影响。如果我们要确认Handlers无论如何都要被正常触发的话,可以使用Ansible-playbook命令执行Playbook时,使用--force-handlers选项来强制要求Handlers可以被正常触发。

4.6.2 部署LAMP服务

    部署一个基于LAMP的应用的第一步就是搭建LAMP服务本身。这是最简单也是最基础的一步。在开始部署之前,我们还需要根据具体要求做一个前期准备工作。我们需要安装Apache、MySQL及PHP到服务器上面,这之前需要解决一些依赖关系。我们需要安装5.5版本PHP软件,但是这个版本的PHP包含在默认的APT源中,所以我们还要添加一个包含PHP5.5版本的外部APT源。

tasks:
- name: Get software for apt repository management.
apt: "name={{ item }} state=present"
with_items:
- python-apt
- python-pycurl

- name: Add ondrej repository for later versions of PHP.
apt_repository: repo='ppa:ondrej/php5-5.6' update_cache=yes

- name: "Install Apache, MySQL, PHP, and other dependencies."
apt: "name={{ item }} state=present"
with_items:
- git
- curl
- sendmail
- apache2
- php5
- php5-common
- php5-mysql
- php5-cli
- php5-curl
- php5-gd
- php5-dev
- php5-mcrypt
- php5-apcu
- php-pear
- python-mysqldb
- mysql-server

- name: Disable the firewall (since this is for local dev only).
service: name=ufw state=stopped

- name: "Start Apache, MySQL, and PHP."
service: "name={{ item }} state=started enabled=yes"
with_items:
- apache2
- mysql


    第一个任务,我们安装了一些让Python能更好的管理APT源的辅助库(因为apt_repository模块需要借助python-apt和python-pycurl这两个工具来实现其功能)。

    第二个任务,由于Ubuntu12.04中默认的APT源是不包含PHP5.4.x及其后续版本的,所以我们安装了包含PHP5.5的ondrej的PHP5-oldstable源。

    第三个任务,安装了所有LAMP服务器需要的软件包,包括运行Drupal框架所需的php5扩展包。

    第四个任务,由于本例基于测试的目的,所以我们禁用了防火墙。如果是在生产环境中,我们需要开启包括22,80和443号端口在内的必需端口。

    第五个任务,启动LAMP所需的各项服务,并确保它们开机启动。

4.6.3 配置Apache

     接下来我们将要配置Apache服务器,使其能与Drupal正常工作。

     Apache默认并未开启mod_rewrite功能,在Ansible中我们可以使用apache2_module这个模块解决。

- name: Enable Apache rewrite module (required for Drupal).
apache2_module: name=rewrite state=present
notify: restart apache

- name: Add Apache virtualhost for Drupal 8 development.
template:
src: "templates/drupal.dev.conf.j2"
dest: "/etc/apache2/sites-available/{{ domain }}.dev.conf"
owner: root
group: root
mode: 0644
notify: restart apache

- name: Symlink Drupal virtualhost to sites-enabled.
file:
src: "/etc/apache2/sites-available/{{ domain }}.dev.conf"
dest: "/etc/apache2/sites-enabled/{{ domain }}.dev.conf"
state: link
notify: restart apache


    第1个任务,使用了Ansible的apache2_module开启Apache的rewrite功能,其原理是将指定的功能所需的模块做一个符号链接,连接到/etc/apache2/mods-enabled目录中。

    第2个任务,是将一个我们事先定义好的Jinja2模块复制到Apache的sites-availiable目录中,同时设置正确的属主、属组及权限。

    第3个任务,是在sites-enabled目录中添加Drupal所需配置文件的符号链接,使得Apache能应用这个配置文件。最后,任务还触发了之前定义的handlers "restart apache"来重新启动Apache,使得新增加的Apache配置文件生效。

    下面我们来看一个Playbook中所提到的Jinja2模块文件dru-pal.dev.conf.j2的内容。

<VirtualHost *:80>
ServerAdmin webmaster@localhost
ServerName {{ domain }}.dev
ServerAlias www.{{ domain }}.dev
DocumentRoot {{ drupal_core_path }}
<Directory "{{ drupal_core_path }}">
Options FollowSymLinks Indexes
AllowOverride All
</Directory>
</VirtualHost>


    可以看到,在标准的Apache VirtualHost的定义代码中使用了一些Jinja2的变量。这种变量的使用方式与我们在Playbook中使用变量的方法是一样的,都是两个大括号包含着变量名,比如:{{variable}}。

    这个模块文件中提到了3个变量(drupal_core_version,drupal_core_path,domain),我们在之前提到的变量配置文件vars.yml中对它们进行定义赋值。

---
# The core version you want to use (e.g. 6.x, 7.x, 8.1.x).
drupal_core_version: "8.1.x"

# The path where Drupal will be downloaded and installed.
drupal_core_path: "/var/www/drupal-{{ drupal_core_version }}-dev"

# The resulting domain will be [domain].dev (with .dev appended).
domain: "drupaltest"


    当Ansible运行到复制模板文件这一步时,模板文件中的Jinja2变量将会自动被替换为我们 在变量文件vars.yml中定义的值。

    此时,Apache已经可以启动了。但是,如果此时我们定义的Drupal的安装目录还未被创建,通常情况下,Apache是要报错的。但是在本例中,我们使用的是Handlers的方式来触发式开启Apache服务,也就是说只有在notify前面的3个任务都运行成功时,用于启动Apache的handler才能被触发。

4.6.4 配置PHP

    lineinfile模块是Ansible编辑文件内容的一大利器。对于PHP的配置,我们就主要借助lineinfile模块来完成。

    通过编辑修改PHP的配置文件,也可以反映出lineinfile模块在编辑文件方面的简单实用。我们来看下面这个编辑PHP配置文件的例子。

- name: Enable upload progress via APC.
lineinfile:
dest: "/etc/php5/apache2/conf.d/20-apcu.ini"
regexp: "^apc.rfc1867"
line: "apc.rfc1867 = 1"
state: present
notify: restart apache


    在这个任务中,我们的目的是开启PHP的apc.rfc1867选项,以确保APC支持上传进度条功能。在任务一开始,我们使用dest关键字告诉lineinfile模块要被编辑的文件的位置及名称,然后使用正则表达式来搜索PHP的配置项apc.rfc1867,接着使用line关键字来指定被匹配到的行具体应该是什么内容,最后,使用state关键字来明确指定我们需要这行内容保留下来。

4.6.5 配置MySQL

    对于本例中的MySQL而言,我们首先需要删除系统默认自带的test数据库,然后为Drupal创建一个新库,我们使用下面一段代码实现:

- name: Remove the MySQL test database.
mysql_db: db=test state=absent

- name: Create a database for Drupal.
mysql_db: "db={{ domain }} state=present"


    MySQL在安装之初都会默认安装一个名为test的测试数据库,在我们使用mysql_secure_installatiion命令来安装MySQL时候,也是会提醒我们对其进行删除的。因为这个库对我们的环境没有用处,所以配置MySQL的第一步就是删除这个库。接下来,我们创建了一个名为{{domain}}的数据库,即以Drupal网站的域名来命名这个库。

注意:对于MySQL而言,Ansible使用Python的MySQLdb模块(python-mysqldb)来对MySQL数据库进行管理,并且默认使用root免密码登录。很显然,这并不符合我们生产环境中的要求。在实际应用中,我们第一步要做的就是修改MySQL的root密码,并限制root账号只能本地登录,同时删除不需要的数据库用户。

4.6.6 安装Drush和Composer

    Drush是一个Drupal命令行工具,是在shell中操作Drupal的桥梁。使用Drush不仅可以快速得到网站状态,还可以对网站进行维护,而且还能很方便的作批处理。Composer是PHP的包管理工具。二者是对PHP网站管理经常用到的工具。使用如下代码可试下Drush和Composer的自动化安装。

- name: Download Composer installer.
get_url:
url: https://getcomposer.org/installer dest: /tmp/composer-installer.php
mode: 0755

- name: Run Composer installer.
command: >
php composer-installer.php
chdir=/tmp
creates=/usr/local/bin/composer

- name: Move Composer into globally-accessible location.
shell: >
mv /tmp/composer.phar /usr/local/bin/composer
creates=/usr/local/bin/composer


    前两个任务下载并安装了Composer,安装之后,在/tmp目录下会生成一个PHP的应用包composer.phr,这个包由第3个任务移到了目录/usr/local/bin下面,并命名为composer,这样一来,我们就可以直接在命令行中使用composer命令来安装Drush的依赖关系了。第2个任务和第3个任务中的creates选项是command模块和Shell模块共有的选项,表示如果文件存在,就不再执行相应的命令。

    接下来通过GitHub下载并安装Drush,

- name: Check out drush master branch.
git:
repo: https://github.com/drush-ops/drush.git dest: /opt/drush

- name: Install Drush dependencies with Composer.
shell: >
/usr/local/bin/composer install
chdir=/opt/drush
creates=/opt/drush/vendor/autoload.php

- name: Create drush bin symlink.
file:
src: /opt/drush/drush
dest: /usr/local/bin/drush
state: link


    在本例中,我们借助了Ansible的git模块从Github上克隆了Drush代码。git模块只需要再用简单的参数repo的dest来分别指定文件在Github上的URL和克隆到本地的存放位置。Drush代码被下载到/opt/drush目录后,在任务二中切换到这个目录下,直接利用composer即可安装。最后在任务三中,为Drush的二进制命令文件创建符号链接,这样就能在命令行中直接使用Drush命令了。

4.6.7 通过git和Drush安装Drupal

    我们通过git来克隆Drupal的代码,并将其保存到我们之前定义的Apache文档根目录(DocumentRoot)中,然后使用Drush来完成最后的安装。

- name: Check out Drupal Core to the Apache docroot.
git:
repo: http://git.drupal.org/project/drupal.git version: "{{ drupal_core_version }}"
dest: "{{ drupal_core_path }}"

- name: Install Drupal dependencies with Composer.
shell: >
/usr/local/bin/composer install
chdir={{ drupal_core_path }}
creates={{ drupal_core_path }}/sites/default/settings.php

- name: Install Drupal.
command: >
drush si -y --site-name="{{ drupal_site_name }}"
--account-name=admin
--account-pass=admin
--db-url=mysql://root@localhost/{{ domain }}
chdir={{ drupal_core_path }}
creates={{ drupal_core_path }}/sites/default/settings.php
notify: restart apache

# SEE: https://drupal.org/node/2121849#comment-8413637 - name: Set permissions properly on settings.php.
file:
path: "{{ drupal_core_path }}/sites/default/settings.php"
mode: 0744

- name: Set permissions properly on files directory.
file:
path: "{{ drupal_core_path }}/sites/default/files"
mode: 0777
state: directory
recurse: yes


    在任务一中,我们从Drupal的git源中下载了Drupal的代码,下载的版本为我们之前在变量中定义的drupal_core_version值。

    在任务三中,我们使用Drush的si命令(site-install的简写)来安装Drupal,在安装过程中同时配置了数据库连接,使用creates来检测重要配置问价settings.php是否被生成,以此判断Drupal是否安装成功,最后触发handlers来重启Apache。

    在vars.yml文件中追加如下代码来为该变量赋值:

# Your Drupal site name.
drupal_site_name: "D8 Test"


4.6.8 Drupal部署过程总结

可以使用ansible-playbook命令来执行这个Playbook。

ansible-playbook playbook.yml




命令执行完后通过访问http://drupaltest.dev(默认已做好域名和IP地址的映射)

playbook.yml

---
- hosts: proxy

vars_files:
- vars.yml

pre_tasks: - name: Update apt cache if needed. apt: update_cache=yes cache_valid_time=3600
handlers:
- name: restart apache
service: name=apache2 state=restarted

tasks:
- name: Get software for apt repository management.
apt: "name={{ item }} state=present"
with_items:
- python-apt
- python-pycurl

- name: Add ondrej repository for later version of PHP
apt_repository: repo='ppa:ondrej/php5' update_cache=yes

- name: "Install Apache, MySQL, PHP, and other dependencies."
apt: "name={{ item }} state=present"
with_items:
- git
- curl
- sendmail
- apache2
- php5
- php5-common
- php5-mysql
- php5-cli
- php5-curl
- php5-gd
- php5-dev
- php5-mcrypt
- php5-apcu
- php-pear
- python-mysqldb
- mysql-server

- name: Disable the firewall (since this is for local dev only).
service: name=ufw state=stopped

- name: "Start Apache, MySQL, and PHP."
service: "name={{ item }} state=started enabled=yes"
with_items:
- apache2
- mysql

- name: Enable Apache rewrite module (required for Drupal).
apache2_module: name=rewrite state=present
notify: restart apache

- name: Add Apache virtualhost for Drupal 8 development.
template:
src: "templates/drupal.dev.conf.j2"
dest: "/etc/apache2/sites-available/{{ domain }}.dev.conf"
owner: root
group: root
mode: 0644
notify: restart apache

- name: Symlink Drupal virtualhost to sites-enabled.
file:
src: "/etc/apache2/sites-available/{{ domain }}.dev.conf"
dest: "/etc/apache2/sites-enabled/{{ domain }}.dev.conf"
state: link
notify: restart apache

- name: Remove default virtualhost file.
file:
path: "/etc/apache2/sites-enabled/000-default"
state: absent
notify: restart apache

- name: Enable upload progress via APC.
lineinfile:
dest: "/etc/php5/apache2/conf.d/20-apcu.ini"
regexp: "^apc.rfc1867"
line: "apc.rfc1867 = 1"
state: present
notify: restart apache

- name: Remove the MySQL test database.
mysql_db: db=test state=absent

- name: Create a database for Drupal.
mysql_db: "db={{ domain }} state=present"

- name: Download Composer installer. get_url: url: https://getcomposer.org/installer dest: /tmp/composer-installer.php mode: 0755 - name: Run Composer installer. command: > php composer-installer.php chdir=/tmp creates=/usr/local/bin/composer - name: Move Composer into globally-accessible location. shell: > mv /tmp/composer.phar /usr/local/bin/composer creates=/usr/local/bin/composer
- name: Check out drush master branch.
git:
repo: https://github.com/drush-ops/drush.git dest: /opt/drush

- name: Install Drush dependencies with Composer.
shell: >
/usr/local/bin/composer install
chdir=/opt/drush
creates=/opt/drush/vendor/autoload.php

- name: Create drush bin symlink.
file:
src: /opt/drush/drush
dest: /usr/local/bin/drush
state: link

- name: Check out Drupal Core to the Apache docroot. git: repo: http://git.drupal.org/project/drupal.git version: "{{ drupal_core_version }}" dest: "{{ drupal_core_path }}" - name: Install Drupal dependencies with Composer. shell: > /usr/local/bin/composer install chdir={{ drupal_core_path }} creates={{ drupal_core_path }}/sites/default/settings.php - name: Install Drupal. command: > drush si -y --site-name="{{ drupal_site_name }}" --account-name=admin --account-pass=admin --db-url=mysql://root@localhost/{{ domain }} chdir={{ drupal_core_path }} creates={{ drupal_core_path }}/sites/default/settings.php notify: restart apache # SEE: https://drupal.org/node/2121849#comment-8413637 - name: Set permissions properly on settings.php. file: path: "{{ drupal_core_path }}/sites/default/settings.php" mode: 0744 - name: Set permissions properly on files directory. file: path: "{{ drupal_core_path }}/sites/default/files" mode: 0777 state: directory recurse: yes
drupal.dev.conf.j2
<VirtualHost *:80>
ServerAdmin webmaster@localhost
ServerName {{ domain }}.dev
ServerAlias www.{{ domain }}.dev
DocumentRoot {{ drupal_core_path }}
<Directory "{{ drupal_core_path }}">
Options FollowSymLinks Indexes
AllowOverride All
</Directory>
</VirtualHost>
vars.yml

---
# The core version you want to use (e.g. 6.x, 7.x, 8.1.x).
drupal_core_version: "8.1.x"

# The path where Drupal will be downloaded and installed.
drupal_core_path: "/var/www/drupal-{{ drupal_core_version }}-dev"

# The resulting domain will be [domain].dev (with .dev appended).
domain: "drupaltest"
# Your Drupal site name. drupal_site_name: "D8 Test"

4.7 实战三:Ansible部署Tomcat实战

    Apache Solr是一种高效可扩展的企业级搜索应用服务器。它易于安装和和配置,而且附带了一个基于HTTP的管理界面。Solr已经在众多大型的网站中使用,较为成熟和稳定。

4.7.1 定义变量并设置Handlers

---
- hosts: proxy

vars_files:
- vars.yml

在相同文件下创建变量文件vars.yml,并在vars.yml中定义如下变量:

---
# 中文测试The directory into which Solr will be downloaded for setup.
download_dir: /tmp
tomcat_version: 8.0.35
tomcat_dir: /opt/tomcat

# The directory inside which Solr will be installed.
solr_dir: /opt/solr

# Solr version and download information.
solr_version: 6.1.0

这5个变量分别定义了软件包的下载存放目录、Tomcat版本号、Tomcat安装路径、Solr安装目录、Solr软件版本号。

接下来定义用于触发式启动Tomcat的Handlers,这里我们使用Ubuntu上的Upstart脚本来管理Tomcat。

handlers:
- name: restart tomcat
command: >
initctl restart tomcat

用来管理Tomcat的Upstart脚本文件为tomcat.conf,需放放置在被管理主机的/etc/init目录下面。其内容如下:

description "Tomcat Server"

start on runlevel [2345]
stop on runlevel [!2345]
respawn
respawn limit 10 5

setuid tomcat
setgid tomcat

env JAVA_HOME=/opt/java
env CATALINA_HOME=/opt/tomcat

# Modify these options as needed
env JAVA_OPTS="-Djava.awt.headless=true -Djava.security.egd=file:/dev/./urandom"
env CATALINA_OPTS="-Xms512M -Xmx1024M -server -XX:+UseParallelGC"

exec $CATALINA_HOME/bin/catalina.sh run

# cleanup temp directory after stop
post-stop script
rm -rf $CATALINA_HOME/temp/*
end script


    接下来的Playbook任务中,通过copy模块将其发送到目标主机的/etc/init目录下。

4.7.2 安装Java

    考虑到网速,先将JDK软件下到服务器上,在用copy模块发送到目标主机。

wget --no-cookies --no-check-certificate --header "Cookie: gpw_e24=http%3A%2F%2Fwww.oracle.com%2F; oraclelicense=accept-securebackup-cookie" "http://download.oracle.com/otn-pub/java/jdk/8u141-b15/336fa29ff2bb4ef291e347e091f7f4a7/jdk-8u141-linux-x64.tar.gz"

- name: Install JDK.
copy: "src={{ item.src }} dest={{ item.dest }}"
with_items:
- src: "./jdk-8u141-linux-x64.tar.gz"
dest: "/tmp/"

- src: "./java.sh"
dest: "/etc/profile.d/"

- name: Create JAVA directory
command: >
mkdir -p /opt/java

- name: expand JDK.
command: >
tar -C /opt/java -xvf {{ download_dir}}/jdk-8u141-linux-x64.tar.gz --strip-components=1

- name: Update alternatives for java.
command: >
update-alternatives --install /usr/bin/java java /opt/java/bin/java 300

- name: Update alternatives for javac.
command: >
update-alternatives --install /usr/bin/javac javac /opt/java/bin/javac 300
java.sh
export JAVA_HOME="/opt/java"
export CLASSPATH=$JAVA_HOME/lib:$JAVA_HOME/jre/lib
export JRE_HOME=${JAVA_HOME}/jre
export PATH=$PATH:$JAVA_HOME/bin

4.7.3 安装Tomcat8

- name: Create Tomcat directory
command: >
mkdir -p {{ tomcat_dir }}

- name: Add Tomcat user
user: "name=tomcat shell=/sbin/nologin"

- name: Download Tomcat.
get_url:
url: "http://apache.fayea.com/tomcat/tomcat-8/v{{ tomcat_version }}/bin/apache-tomcat-{{ tomcat_version}}.tar.gz"
dest: "{{ download_dir }}/apache-tomcat-{{ tomcat_version }}.tar.gz"

- name: Expand Tomcat.
command: >
tar -C {{ tomcat_dir}} -xvf {{ download_dir }}/apache-tomcat-{{ tomcat_version }}.tar.gz --strip-components=1
creates={{ tomcat_dir }}/conf/server.xml

- name: Rload Upstart Configuration
command: initctl reload-configuration

4.7.4 安装Apache Solr

- name: Download Solr.
get_url:
url: "http://apache.fayea.com/lucene/solr/{{ solr_version }}/solr-{{ solr_version }}.tgz"
dest: "{{ download_dir }}/solr-{{ solr_version }}.tgz"

- name: Move Solr into place
command: >
mkdir -p {{ solr_dir }}

- name: Expand Solr.
command: >
tar -C {{ solr_dir }} -xvzf {{ download_dir }}/solr-{{ solr_version }}.tgz --strip-components=1
creates={{ solr_dir }}/dist/solr-core-{{ solr_version }}.jar


    Solr软件的安装方法和Tomcat很像,解压后不需要编译,直接将软件包放在指定的位置并对配置文件稍作修改即可。
- name: Move Solr components into place.
shell: >
rsync -av {{ item.src }} {{ item.dest }}
creates={{ item.creates }}
with_items:
- src: "{{ solr_dir }}/server/solr-webapp/webapp/*"
dest: "{{ tomcat_dir }}/webapps/solr"
creates: "/opt/tomcat/webapps/solr/index.html"

- src: "{{ solr_dir }}/server/lib/ext/*"
dest: "{{ tomcat_dir }}/webapps/solr/WEB-INF/lib/"
creates: "/opt/tomcat/webapps/solr/WEB-INF/lib/slf4j-api-1.7.7.jar"

- src: "{{ solr_dir }}/server/resources/log4j.properties"
dest: "{{ tomcat_dir }}/webapps/solr/WEB-INF/classes/"
creates: "/opt/tomcat/webapps/solr/WEB-INF/classes/log4j.properties"


    上面这个任务将Tomcat所需的Solr的页面文件和库文件全部部署到位,同样使用shell模块的creates选项来预先判断文件是否已经存在,以减少重复操作。
    下一步,我们需要修改Solr的配置文件/path/to/tomcat/webapps/solr/WEB-INF/web.xml,修改其中的<env-entry>部分,来指定Solr的Home目录。修改内容如下:

<env-entry>
<env-entry-name>solr/home</env-entry-name>
<env-entry-value>/opt/solr/server/solr</env-entry-value>
<env-entry-type>java.lang.String</env-entry-type>
</env-entry>


    发送给目标主机,更改属组属主。
- name: Overwrite Solr configure file
copy: "src=./web.xml dest=/opt/tomcat/webapps/solr/WEB-INF/web.xml"

- name: Changem permission
file:
path: "/opt"
owner: tomcat
group: tomcat
recurse: yes
notify: restart tomcat
在浏览器中选择文件http://solr.example.com:8080/solr/index.html(假设你的目标主机的域名为solr.example.com),就可以看到Solr的管理页面了。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: