提供:ZStack社区
本教程是《用Ansible自动化PHP部署》系列文章的第一篇。
本教程介绍如何使用Ansible部署一个简单的PHP应用。到本文结束时,你将有一台搭建在VPS(云主机)上的Web服务器,上面运行着一个简单的PHP,而整个过程完全不需要你SSH到Web服务器所在的VPS敲命令。
我们的PHP应用使用的是Laravel框架。如果你用的是其他框架,整个操作过程其实也差不多。
我们将使用Ansible在一个Ubuntu 14.04云主机上安装配置Nginx、PHP以及其他服务。你需要对Ansible有一定的了解。如果你对Ansible不太熟,可以先参阅这篇Ansible基础教程。
在开始操作之前,你需要满足如下条件:
your_server_ip
指代这个IP地址。Ansible的安装可以很简单的通过PPA来做。安装了相应的PPA后直接使用apt
安装Ansible。
首先,用apt-add-repository
命令添加ppa:
sudo apt-add-repository ppa:ansible/ansible
然后更新一下apt
缓存:
sudo apt-get update
然后安装Ansible:
sudo apt-get install ansible
Ansible装好之后,创建一个新的目录,在里面做一些基本设置。Ansible默认使用的hosts文件位置在/etc/ansible/hosts
,这里面列出了其管理的所有服务器。这个文件是全局性的,不是我们想要的;我们想要一个本地的,就需要自己创建。
我们直接在自己的工作目录下创建一个新的Ansible配置文件,这个文件会告诉Ansible在同一目录下查找hosts文件。
首先,创建一个新目录:
mkdir ~/ansible-php
进去:
cd ~/ansible-php/
创建一个ansible.cfg
文件,用nano
或者其他编辑器打开它:
nano ansible.cfg
在[defaults]
组下添加一个hostfile
配置选项,将该项赋值为hosts
。简单来说就是把下面的内容复制到你的文件里:
ansible.cfg
[defaults]
hostfile = hosts
保存退出。然后,创建hosts
文件。这里面将列出你要控制管理的PHP云主机的IP。
nano hosts
把下面的内容复制粘贴到hosts文件里。里面有一个php
组,下面设置了你的服务器ipyour_server_ip
以及你在这台PHP服务器上的非root用户名sammy
(别忘了替换成你自己的IP和用户名):
hosts
[php]
your_server_ip ansible_ssh_user=sammy
保存退出。现在来做个简单的检查,看看Ansible是否能够连接到上面这个服务器。Ping一下php看看:
ansible php -m ping
如果你是第一次从本机登陆,可能会返回SSH认证的提示。SSH认证完成后,这次ping应该返回一个成功的结果,看起来差不多这样:
111.111.111.111 | success >> {
"changed": false,
"ping": "pong"
}
Ansible的安装配置这样就可以了。现在去设置我们的Web服务器。
我们将使用Ansible和apt
来在Web服务器上安装一些系统软件包,包括git
、nginx
、sqlite3
、mcrypt
、以及一些php5-*
的包。
在安装上述软件包之前,我们需要先创建一个简单的playbook。创建一个php.yml
作为我们的playbook:
nano php.yml
把下面的配置内容复制粘贴进去。前两行设定了hosts组(php
)以及运行该hosts组的默认权限sudo
。下面的内容添加了我们所需要的软件包。如果你想要直接安装自己的应用,则可以修改这部分内容;如果你想先跟着我们安装我们的Laravel应用,则可以照单全收:
---
- hosts: php
sudo: yes
tasks:
- name: install packages
apt: name={{ item }} update_cache=yes state=latest
with_items:
- git
- mcrypt
- nginx
- php5-cli
- php5-curl
- php5-fpm
- php5-intl
- php5-json
- php5-mcrypt
- php5-sqlite
- sqlite3
保存退出。最后,运行ansible-playbook
命令,该命令会在目标云主机上安装所有的软件包。如果你的目标云主机是通过密码登陆的,则需要在命令里包含--ask-sudo-pass
选项。
ansible-playbook php.yml --ask-sudo-pass
本章将修改PHP云主机上的一些系统配置文件。最重要的一个配置(不包括Nginx的配置文件,Nginx将在下一章节中介绍)是php5-fpm
的cgi.fix_pathinfo
选项,因为这个选项的默认值会造成安全隐患。
我先简单介绍一下整个步骤,然后贴出整个php.yml
以方便复制粘贴。
Lineinfile模块可用于确保配置的值完全符合我们的期待,这需要用到正则表达式让Ansible定位到我们想要修改的参数。修改之后,我们需要重启php5-fpm
和nginx
让配置生效,这需要用两个handler来实现。Handler很适合做此类任务,不仅因为它们精确的触发时机(任务变更时),而且因为它们在playbook的末尾执行,所以多个任务可以调用同一个handler,每一个handler仅会执行一次。
这部分内容看起来是这样的:
- name: ensure php5-fpm cgi.fix_pathinfo=0
lineinfile: dest=/etc/php5/fpm/php.ini regexp='^(.*)cgi.fix_pathinfo=' line=cgi.fix_pathinfo=0
notify:
- restart php5-fpm
- restart nginx
handlers:
- name: restart php5-fpm
service: name=php5-fpm state=restarted
- name: restart nginx
service: name=nginx state=restarted
注:Ansible 1.9.1版有一个bug,导致php5-fpm
无法通过上面的service
模块进行重启。临时的解决办法是改用shell
命令,像这样:
- name: restart php5-fpm
shell: service php5-fpm restart
然后,我们需要确保php5-mcrypt
模块处于开启状态(enabled)。这需要运行php5enmod
脚本,检查20-mcrypt.ini
文件是否在正确的位置。这里,我们的逻辑是让Ansible建立的任务尝试创建一个指定的文件,若文件已存在则不执行任务。
- name: enable php5 mcrypt module
shell: php5enmod mcrypt
args:
creates: /etc/php5/cli/conf.d/20-mcrypt.ini
就是这样。现在可以打开php.yml
把上面的内容都粘贴进去了:
nano php.yml
整个文件现在看起来应该是这样的:
---
- hosts: php
sudo: yes
tasks:
- name: install packages
apt: name={{ item }} update_cache=yes state=latest
with_items:
- git
- mcrypt
- nginx
- php5-cli
- php5-curl
- php5-fpm
- php5-intl
- php5-json
- php5-mcrypt
- php5-sqlite
- sqlite3
- name: ensure php5-fpm cgi.fix_pathinfo=0
lineinfile: dest=/etc/php5/fpm/php.ini regexp='^(.*)cgi.fix_pathinfo=' line=cgi.fix_pathinfo=0
notify:
- restart php5-fpm
- restart nginx
- name: enable php5 mcrypt module
shell: php5enmod mcrypt
args:
creates: /etc/php5/cli/conf.d/20-mcrypt.ini
handlers:
- name: restart php5-fpm
service: name=php5-fpm state=restarted
- name: restart nginx
service: name=nginx state=restarted
最后,运行playbook:
ansible-playbook php.yml --ask-sudo-pass
我们的Web服务器主机上现在已经安装了所有需要的软件包,并完成了基本的系统配置。
本章将把Laravel框架的repo给clone到Web服务器主机上。跟上一章一样,我先一步一步的说明,最后再给出完整的php.yml
供复制粘贴。
在进行git repo的clone之前,先确认一下/var/www
是否存在。这可以用file模块创建的任务来实现:
- name: create /var/www/ directory
file: dest=/var/www/ state=directory owner=www-data group=www-data mode=0700
如上所述,我们需要用git模块来进行repo的拉取,整个进程只需要一个git clone
命令所需的repo源地址。我们还需要定义clone的目标目录,并在目标目录已经存在该repo时让Ansible取消拉取(设置update=no
)。Laravel的Git repo url是:https://github.com/laravel/laravel.git
。
另外,我们需要以www-data
用户来执行这个任务以确保权限的正确性,这就需要Ansible以sudo
命令切换到另一个用户下执行此命令。这部分内容看起来是这样的:
- name: Clone git repository
git: >
dest=/var/www/laravel
repo=https://github.com/laravel/laravel.git
update=no
sudo: yes
sudo_user: www-data
注:如果是基于SSH的repo,SSH host的验证过程可能会干扰任务的执行。可以添加一个accept_hostkey=yes
配置项来避免这一情况。
就是这样。现在打开php.yml
开始复制粘贴:
nano php.yml
前面的我就不贴了,只把本章内容放进来,如下(记得把handler留在文件末尾):
...
- name: enable php5 mcrypt module
shell: php5enmod mcrypt
args:
creates: /etc/php5/cli/conf.d/20-mcrypt.ini
- name: create /var/www/ directory
file: dest=/var/www/ state=directory owner=www-data group=www-data mode=0700
- name: Clone git repository
git: >
dest=/var/www/laravel
repo=https://github.com/laravel/laravel.git
update=no
sudo: yes
sudo_user: www-data
handlers:
- name: restart php5-fpm
service: name=php5-fpm state=restarted
- name: restart nginx
service: name=nginx state=restarted
保存退出,运行之:
ansible-playbook php.yml --ask-sudo-pass
本章将使用Composer安装PHP应用以及其依赖项。
Composer有一个create-project
命令,它可以安装所有需要的依赖项,并运行composer.json
文件下post-create-project-cmd
部分定义的项目创建动作。这可以确保应用安装的正确性。
我们可以用下面的这个Ansible任务来下载安装Composer到全局目录/usr/local/bin/composer
。这样一来,访问这台云主机的其他人和应用也都可以使用Composer。
- name: install composer
shell: curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer
args:
creates: /usr/local/bin/composer
安装Composer之后,我们就可以使用Composer模块了。我们将告诉Composer我们的项目位置(working_dir
参数),然后运行create-project
命令。我们还需要添加optimize_autoloader=no
参数,因为create-project
命令不支持这个标记。跟git
命令一样,我们需要用www-data
用户来执行这个任务以确保权限正确。这部分内容看起来是这样的:
- name: composer create-project
composer: command=create-project working_dir=/var/www/laravel optimize_autoloader=no
sudo: yes
sudo_user: www-data
注:create-project
在一台刚建立的新主机上可能需要运行很久的时间,因为Composer缓存尚未建立,需要从头下载所有的东西。
就这样。现在打开php.yml
文件开始复制粘贴……
nano php.yml
跟上一章一样,别忘了把handlers
留在最后:
...
- name: Clone git repository
git: >
dest=/var/www/laravel
repo=https://github.com/laravel/laravel.git
update=no
sudo: yes
sudo_user: www-data
- name: install composer
shell: curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer
args:
creates: /usr/local/bin/composer
- name: composer create-project
composer: command=create-project working_dir=/var/www/laravel optimize_autoloader=no
sudo: yes
sudo_user: www-data
handlers:
- name: restart php5-fpm
service: name=php5-fpm state=restarted
- name: restart nginx
service: name=nginx state=restarted
保存退出,运行playbook:
ansible-playbook php.yml --ask-sudo-pass
这儿有个问题:如果我们现在再运行一次Ansible,会发生什么事呢?
composer create-project
会再运行一次。就Laravel而言,这会生成一个新的APP_KEY
。所以,我们需要该任务仅在新clone之后运行一次。要实现这个效果,可以在git clone
任务的运行结果上注册一个变量,然后在composer create-project
任务里检查这个变量。如果git clone
那儿变了,则运行composer create-project
;否则,则跳过。
打开php.yml
进行编辑:
nano php.yml
找到git clone
的任务,在下面添加一个register
选项,将任务执行的结果保存为cloned
变量:
- name: Clone git repository
git: >
dest=/var/www/laravel
repo=https://github.com/laravel/laravel.git
update=no
sudo: yes
sudo_user: www-data
register: cloned
再找到composer create-project
任务,在下面添加一个when
选项,检查cloned
变量是否为“changed”:
- name: composer create-project
composer: command=create-project working_dir=/var/www/laravel optimize_autoloader=no
sudo: yes
sudo_user: www-data
when: cloned|changed
保存退出,运行playbook:
ansible-playbook php.yml --ask-sudo-pass
现在运行的Composer就不会改变APP_KEY
了。
注:有些版本的Ansible的composer
似乎有bug,会忽略脚本的执行,其输出是“OK”而不是“Changed”,但却不会安装依赖项。
本章将为应用更新一些环境变量。
Laravel有一个默认的.env
文件,其中将APP_ENV
设为local
,APP_DEBUG
设为true
。我们需要把local
改为production
,将true
改为false
。这个需求同样可以用lineinfile
模块来实现:
- name: set APP_DEBUG=false
lineinfile: dest=/var/www/laravel/.env regexp='^APP_DEBUG=' line=APP_DEBUG=false
- name: set APP_ENV=production
lineinfile: dest=/var/www/laravel/.env regexp='^APP_ENV=' line=APP_ENV=production
打开php.yml
进行编辑:
nano php.yml
把上面的代码粘贴进来(handler部分还是在末尾):
...
- name: composer create-project
composer: command=create-project working_dir=/var/www/laravel optimize_autoloader=no
sudo: yes
sudo_user: www-data
when: cloned|changed
- name: set APP_DEBUG=false
lineinfile: dest=/var/www/laravel/.env regexp='^APP_DEBUG=' line=APP_DEBUG=false
- name: set APP_ENV=production
lineinfile: dest=/var/www/laravel/.env regexp='^APP_ENV=' line=APP_ENV=production
handlers:
- name: restart php5-fpm
service: name=php5-fpm state=restarted
- name: restart nginx
service: name=nginx state=restarted
保存退出,运行之:
ansible-playbook php.yml --ask-sudo-pass
lineinfile
模块很适合做这种快速修改文本文件的工作,设置环境变量这种工作用它来做再好不过。
本章将配置Nginx用于服务我们的PHP应用。
现在如果你在浏览器里访问你的云主机IP http://your_server_ip/
,你看到的是Nginx的默认页面,而不是我们的Laravel项目页面。这是因为我们的Nginx还没有被设置为从/var/www/laravel/public
目录服务内容。这则设置需要更改Nginx的默认配置文件。另外,我们还需要添加php-fpm
以让Nginx能够处理PHP脚本。
创建一个nginx.conf
文件:
nano nginx.conf
把下面这些代码粘贴进去。有关Nginx配置的详细介绍可以参考这篇LNMP配置教程的第四章。以下内容设置了Laravel所在的公共目录,定义了Nginx使用的server_name
跟我们之前在hosts
文件中填写的hostname一致(通过inventory_hostname
变量调用)。
nginx.conf
server {
listen 80 default_server;
listen [::]:80 default_server ipv6only=on;
root /var/www/laravel/public;
index index.php index.html index.htm;
server_name {{ inventory_hostname }};
location / {
try_files $uri $uri/ =404;
}
error_page 404 /404.html;
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /var/www/laravel/public;
}
location ~ \.php$ {
try_files $uri =404;
fastcgi_split_path_info ^(.+\.php)(/.+)$;
fastcgi_pass unix:/var/run/php5-fpm.sock;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
}
保存退出。
现在,我们用template
模块推送上述配置文件。template
模块跟copy
模块看起来挺像,但两者之间有个关键的区别:copy
在复制文件(一个或多个)的时候不进行任何变动,而template
则会在复制文件(一个)的时候把所有的变量填进去。我们刚才在配置文件里用到了{{ inventory_hostname }}
变量,所以当我们使用template
模块时,该模块会把我们填写在hosts
文件里的那个IP地址替换进来。这样就不用在配置文件里面进行硬编码了。
另外,因为我们更改了Nginx的配置,我们需要重启Nginx和php-fpm
(编写任务的时候经常需要考虑到此类需求)。我们用notify
来实现这一需求。
- name: Configure nginx
template: src=nginx.conf dest=/etc/nginx/sites-available/default
notify:
- restart php5-fpm
- restart nginx
就这样。现在再次打开我们的php.yml
文件,开始复制粘贴:
nano php.yml
整个文件内容应该是这个样子的:
php.yml
---
- hosts: php
sudo: yes
tasks:
- name: install packages
apt: name={{ item }} update_cache=yes state=latest
with_items:
- git
- mcrypt
- nginx
- php5-cli
- php5-curl
- php5-fpm
- php5-intl
- php5-json
- php5-mcrypt
- php5-sqlite
- sqlite3
- name: ensure php5-fpm cgi.fix_pathinfo=0
lineinfile: dest=/etc/php5/fpm/php.ini regexp='^(.*)cgi.fix_pathinfo=' line=cgi.fix_pathinfo=0
notify:
- restart php5-fpm
- restart nginx
- name: enable php5 mcrypt module
shell: php5enmod mcrypt
args:
creates: /etc/php5/cli/conf.d/20-mcrypt.ini
- name: create /var/www/ directory
file: dest=/var/www/ state=directory owner=www-data group=www-data mode=0700
- name: Clone git repository
git: >
dest=/var/www/laravel
repo=https://github.com/laravel/laravel.git
update=no
sudo: yes
sudo_user: www-data
register: cloned
- name: install composer
shell: curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer
args:
creates: /usr/local/bin/composer
- name: composer create-project
composer: command=create-project working_dir=/var/www/laravel optimize_autoloader=no
sudo: yes
sudo_user: www-data
when: cloned|changed
- name: set APP_DEBUG=false
lineinfile: dest=/var/www/laravel/.env regexp='^APP_DEBUG=' line=APP_DEBUG=false
- name: set APP_ENV=production
lineinfile: dest=/var/www/laravel/.env regexp='^APP_ENV=' line=APP_ENV=production
- name: Configure nginx
template: src=nginx.conf dest=/etc/nginx/sites-available/default
notify:
- restart php5-fpm
- restart nginx
handlers:
- name: restart php5-fpm
service: name=php5-fpm state=restarted
- name: restart nginx
service: name=nginx state=restarted
保存退出,运行之:
ansible-playbook php.yml --ask-sudo-pass
跑完后,进入浏览器看看你的云主机IP,应该能看到Laravel的项目页面啦!
本文部署了一个公共repo的PHP应用。这对于学习Ansible的用法是足够了,然而在实际工作中,我们经常会需要与私有的git repo打交道,这会涉及SSH密钥验证的步骤(上述步骤3里提到过这个问题)。
你可以用Ansible来复制你的SSH密钥,放在git clone
任务之前:
- name: create /var/www/.ssh/ directory
file: dest=/var/www/.ssh/ state=directory owner=www-data group=www-data mode=0700
- name: copy private ssh key
copy: src=deploykey_rsa dest=/var/www/.ssh/id_rsa owner=www-data group=www-data mode=0600
这样就可以正确的完成验证并部署应用了。
至此,你已经成功的在一台基于Ubuntu的Nginx服务器上部署了一个简单的PHP应用,并使用Composer管理起来所有依赖,而整个过程完全不用手动登入这台云主机!
本文来源自DigitalOcean Community。英文原文:How To Deploy a Basic PHP Application Using Ansible on Ubuntu 14.04 by Stephen Rees-Carter
翻译:lazycai