在Ubuntu 14.04上使用Ansible部署PHP应用,14.04ansible


在Ubuntu 14.04上使用Ansible部署PHP应用

提供:ZStack社区

教程系列

本教程是《用Ansible自动化PHP部署》系列文章的第一篇。

前言

本教程介绍如何使用Ansible部署一个简单的PHP应用。到本文结束时,你将有一台搭建在VPS(云主机)上的Web服务器,上面运行着一个简单的PHP,而整个过程完全不需要你SSH到Web服务器所在的VPS敲命令。

我们的PHP应用使用的是Laravel框架。如果你用的是其他框架,整个操作过程其实也差不多。

准备工作

我们将使用Ansible在一个Ubuntu 14.04云主机上安装配置Nginx、PHP以及其他服务。你需要对Ansible有一定的了解。如果你对Ansible不太熟,可以先参阅这篇Ansible基础教程。

在开始操作之前,你需要满足如下条件:

  • 一台安装了Ubuntu 14.04的云主机,硬盘大小随意,需要有IP地址。本教程将使用your_server_ip指代这个IP地址。
  • 另一台安装了Ubuntu 14.04的云主机,将用于安装Ansible。整个登陆操作都将在这一台云主机上进行。
  • 两台云主机都设置了可以sudo的非root用户。
  • Ansible云主机上配置好SSH密钥。

步骤1:安装Ansible

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服务器。

步骤2:安装所需的软件包

我们将使用Ansible和apt来在Web服务器上安装一些系统软件包,包括gitnginxsqlite3mcrypt、以及一些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

步骤3:修改系统配置文件

本章将修改PHP云主机上的一些系统配置文件。最重要的一个配置(不包括Nginx的配置文件,Nginx将在下一章节中介绍)是php5-fpmcgi.fix_pathinfo选项,因为这个选项的默认值会造成安全隐患。

我先简单介绍一下整个步骤,然后贴出整个php.yml以方便复制粘贴。

Lineinfile模块可用于确保配置的值完全符合我们的期待,这需要用到正则表达式让Ansible定位到我们想要修改的参数。修改之后,我们需要重启php5-fpmnginx让配置生效,这需要用两个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服务器主机上现在已经安装了所有需要的软件包,并完成了基本的系统配置。

步骤4:获取Git Repo

本章将把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

步骤5:用Composer创建应用

本章将使用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”,但却不会安装依赖项。

步骤6:更新环境变量

本章将为应用更新一些环境变量。

Laravel有一个默认的.env文件,其中将APP_ENV设为localAPP_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模块很适合做这种快速修改文本文件的工作,设置环境变量这种工作用它来做再好不过。

步骤7:设置Nginx

本章将配置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

相关内容

    暂无相关文章