介绍
本教程是关于在Ubuntu 14.04上使用Ansible部署PHP应用程序的系列的第二部分。 在第一个教程涵盖了部署应用程序的基本步骤,是本教程中列出的步骤的起点。
在本教程中,我们将介绍设置SSH密钥以支持代码部署/发布工具,配置系统防火墙,配置和配置数据库(包括密码!),以及设置任务调度程序(cron)和队列守护程序。 本教程结束时的目标是让您拥有一个具有上述高级配置的完全正常的PHP应用程序服务器。
就像过去的教程中,我们将使用Laravel框架作为示例PHP应用程序。 但是,如果您已经拥有自己的框架和应用程序,则可以轻松修改这些指令以支持其他框架和应用程序。
先决条件
本教程直接从年底是继该系列中的第一个教程 ,所有的配置和该教程中生成的文件是必需的。 如果您还没有完成该教程,请先继续,然后再继续学习本教程。
第1步 - 切换应用程序存储库
在这一步中,我们将把Git仓库更新为一个稍微自定义的示例仓库。
因为默认的Laravel安装不需要我们将在本教程中设置的高级功能,我们将把现有的存储库从标准存储库切换到添加了一些调试代码的示例存储库,只是为了显示什么时候工作。 我们将使用该库位于https://github.com/do-community/do-ansible-adv-php
。
如果你还没有这样做的话,将目录切换到ansible-php
从以前的教程。
cd ~/ansible-php/
打开我们现有的剧本进行编辑。
nano php.yml
找到并更新“克隆git存储库”任务,因此它看起来像这样。
- name: Clone git repository
git: >
dest=/var/www/laravel
repo=https://github.com/do-community/do-ansible-adv-php
update=yes
version=example
sudo: yes
sudo_user: www-data
register: cloned
保存并运行剧本。
ansible-playbook php.yml --ask-sudo-pass
当它运行完毕后,访问你的服务器在Web浏览器(如http:// your_server_ip /
)。 您应该看到一个消息,说“找不到驱动程序”。
这意味着我们已成功替换了示例存储库的默认存储库,但应用程序无法连接到数据库。 这是我们期望在这里看到的,我们将在教程中稍后安装和设置数据库。
第2步 - 设置部署的SSH密钥
在此步骤中,我们将设置可用于应用程序代码部署脚本的SSH密钥。
虽然Ansible是伟大的维护配置和设置服务器和应用程序,如工具特使和火箭人常常被用来推动代码更改到你的服务器,并运行应用程序远程命令。 大多数这些工具需要一个SSH连接,可以直接访问应用程序安装。 在我们的例子中,这意味着我们需要配置SSH密钥为www-data
的用户。
我们将需要您希望推送代码的用户的公钥文件。 此文件通常在找到~/.ssh/id_rsa.pub
。 该文件复制到ansible-php
目录。
cp ~/.ssh/id_rsa.pub ~/ansible-php/deploykey.pub
我们可以使用Ansible authorized_key
模块安装在我们的公钥/var/www/.ssh/authorized_keys
,这将使部署工具来连接并访问我们的应用程序。 配置仅需要知道的关键的是,使用查找,密钥需要用户安装用于( www-data
中的情况)。
- name: Copy public key into /var/www
authorized_key: user=www-data key="{{ lookup('file', 'deploykey.pub') }}"
我们还需要设置www-data
用户的shell,所以我们实际上可以登录,否则SSH将允许连接,但不会有呈现给用户的shell。 这可以通过使用来完成user
模块,与外壳设置/bin/bash
(或您的首选shell)。
- name: Set www-data user shell
user: name=www-data shell=/bin/bash
现在,打开剧本进行编辑,以添加到新任务中。
nano php.yml
添加上述任务到你的php.yml
剧本; 文件的结尾应该匹配以下。 添加以红色突出显示。
. . .
- name: Configure nginx
template: src=nginx.conf dest=/etc/nginx/sites-available/default
notify:
- restart php5-fpm
- restart nginx
- name: Copy public key into /var/www
authorized_key: user=www-data key="{{ lookup('file', 'deploykey.pub') }}"
- name: Set www-data user shell
user: name=www-data shell=/bin/bash
handlers:
. . .
保存并运行剧本。
ansible-playbook php.yml --ask-sudo-pass
当Ansible完成后,你应该能够使用SSH到www-data
的用户。
ssh www-data@your_server_ip
如果你成功登录,它的工作! 现在,您可以通过输入登录背出logout
或按CTRL + D。
对于本教程中的任何其他步骤,我们不需要使用该连接,但如果您正在设置其他工具(如上所述),或者根据需要进行常规调试和应用程序维护,这将非常有用。
第3步 - 配置防火墙
在此步骤中,我们将在服务器上配置防火墙,以仅允许HTTP和SSH的连接。
Ubuntu的14.04自带的默认安装UFW( 单纯性防火墙 ),并Ansible与支持它ufw
模块。 它具有许多强大的功能,并被设计为尽可能简单。 它非常适合仅需要打开几个端口的自包含Web服务器。 在我们的例子中,我们想要端口80(HTTP)和端口22(SSH)打开。 您还可能需要端口443用于HTTPS。
该ufw
模块有许多其中执行不同的任务不同的选择。 我们需要执行的不同任务是:
默认情况下,启用UFW并拒绝所有传入流量。
打开SSH端口但速率限制它以防止暴力攻击。
打开HTTP端口。
这可以分别通过以下任务来完成。
- name: Enable UFW
ufw: direction=incoming policy=deny state=enabled
- name: UFW limit SSH
ufw: rule=limit port=ssh
- name: UFW open HTTP
ufw: rule=allow port=http
和以前一样,打开php.yml
文件进行编辑。
nano php.yml
将以上任务添加到剧本中; 文件的结尾应该匹配以下。
. . .
- name: Copy public key into /var/www
authorized_key: user=www-data key="{{ lookup('file', 'deploykey.pub') }}"
- name: Set www-data user shell
user: name=www-data shell=/bin/bash
- name: Enable UFW
ufw: direction=incoming policy=deny state=enabled
- name: UFW limit SSH
ufw: rule=limit port=ssh
- name: UFW open HTTP
ufw: rule=allow port=http
handlers:
. . .
保存并运行剧本。
ansible-playbook php.yml --ask-sudo-pass
成功完成后,您仍然可以通过SSH(使用Ansible)或HTTP连接到您的服务器; 其他端口现在将被阻止。
您可以通过运行以下命令随时验证UFW的状态:
ansible php --sudo --ask-sudo-pass -m shell -a "ufw status verbose"
分解上面的Ansible命令:
-
ansible
:运行原始Ansible任务,没有剧本。 -
php
:运行对这一群体中的主机的任务。 -
--sudo
:运行命令sudo
。 -
--ask-sudo-pass
:提示输入sudo
密码。 -
-m shell
:运行shell
模块。 -
-a "ufw status verbose"
:该选项被传递到模块。 因为它是一个shell
命令,我们通过原始的命令(即ufw status verbose
)直,没有任何key=value
选项。
它应该返回这样的东西。
your_server_ip | success | rc=0 >>
Status: active
Logging: on (low)
Default: deny (incoming), allow (outgoing), disabled (routed)
New profiles: skip
To Action From
-- ------ ----
22 LIMIT IN Anywhere
80 ALLOW IN Anywhere
22 (v6) LIMIT IN Anywhere (v6)
80 (v6) ALLOW IN Anywhere (v6)
第4步 - 安装MySQL包
在这一步中,我们将为我们的应用程序设置一个MySQL数据库。
第一步是确保MySQL安装在我们的服务器上,只需将所需的软件包添加到我们剧本顶部的安装软件包任务中即可。 我们需要的软件包mysql-server
, mysql-client
,和php5-mysql
。 我们还需要python-mysqldb
这样Ansible可以与MySQL进行通信。
当我们添加软件包,我们需要重启nginx
和php5-fpm
以确保新的封装应用程序使用。 在这种情况下,我们需要MySQL可用于PHP,因此它可以连接到数据库。
Ansible的一个奇妙的事情是,你可以修改任何任务,重新运行你的剧本,并将应用更改。 这包括选项列表,就像我们与apt
的任务。
和以前一样,打开php.yml
文件进行编辑。
nano php.yml
找到install packages
任务,更新,以包括上面的包:
. . .
- 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
- mysql-server
- mysql-client
- php5-mysql
- python-mysqldb
notify:
- restart php5-fpm
- restart nginx
. . .
保存并运行剧本:
ansible-playbook php.yml --ask-sudo-pass
第5步 - 设置MySQL数据库
在这一步中,我们将为我们的应用程序创建一个MySQL数据库。
Ansible可以通过直接与MySQL的mysql_
-prefaced模块(如mysql_db
, mysql_user
)。 该mysql_db
模块提供了一种方法,以确保数据库具有特定名称的存在,所以我们可以使用一个任务是这样来创建数据库。
- name: Create MySQL DB
mysql_db: name=laravel state=present
我们还需要一个具有已知密码的有效用户帐户,以允许我们的应用程序连接到数据库。 一种方法是在本地生成密码并将其保存在我们的Ansible playbook中,但这是不安全的,有一个更好的方法。
我们将在服务器上使用Ansible生成密码,并在需要的地方直接使用它。 要生成一个密码,我们将使用makepasswd
命令行工具,并要求32个字符的密码。 由于makepasswd
是不是在Ubuntu上违约,我们将需要在添加到软件包列表了。
我们还会告诉Ansible记住命令的输出(即密码),所以我们以后可以在我们的剧本中使用它。 然而,由于Ansible不知道,如果它已经运行shell
命令,我们还会当我们运行这个命令创建一个文件。 Ansible将检查文件是否存在,如果存在,它将假设命令已经运行,并且不会再次运行它。
任务如下所示:
- name: Generate DB password
shell: makepasswd --chars=32
args:
creates: /var/www/laravel/.dbpw
register: dbpwd
接下来,我们需要使用我们指定的密码创建实际的MySQL数据库用户。 这是使用进行mysql_user
模块,我们可以使用stdout
上,我们的密码生成任务期间定义得到shell命令的原始输出,这样的变量选项: dbpwd.stdout
。
所述mysql_user
命令接受用户的名称和所需的特权。 在我们的例子中,我们要创建一个用户调用laravel
,让他们在完全权限laravel
表。 我们还需要告诉只有当运行任务dbpwd
变量发生了变化 ,在运行密码生成任务时将只。
任务应如下所示:
- name: Create MySQL User
mysql_user: name=laravel password={{ dbpwd.stdout }} priv=laravel.*:ALL state=present
when: dbpwd.changed
把这个一起,打开php.yml
文件进行编辑,这样我们就可以在上面添加任务。
nano php.yml
首先,找到install packages
任务,并更新到包括makepasswd
包。
. . .
- 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
- mysql-server
- mysql-client
- php5-mysql
- python-mysqldb
- makepasswd
notify:
- restart php5-fpm
- restart nginx
. . .
然后,在底部添加密码生成,MySQL数据库创建和用户创建任务。
. . .
- name: UFW limit SSH
ufw: rule=limit port=ssh
- name: UFW open HTTP
ufw: rule=allow port=http
- name: Create MySQL DB
mysql_db: name=laravel state=present
- name: Generate DB password
shell: makepasswd --chars=32
args:
creates: /var/www/laravel/.dbpw
register: dbpwd
- name: Create MySQL User
mysql_user: name=laravel password={{ dbpwd.stdout }} priv=laravel.*:ALL state=present
when: dbpwd.changed
handlers:
. . .
不要运行的剧本呢!你可能已经注意到,尽管我们已经创建了MySQL用户和数据库,我们没有做的任何密码。 我们将在下一步中介绍。 当使用shell
内Ansible任务,它始终是重要要记住,完成与输出交易的整个工作流程/运行它,以避免手动登录并重置前的状态任务的结果。
第6步 - 为数据库配置PHP应用程序
在此步骤中,我们将保存MySQL数据库密码进入.env
应用程序文件。
就像我们在上一个教程一样,我们将更新.env
文件,包括我们新创建的数据库凭据。 默认情况下Laravel的.env
文件包含这些行:
DB_HOST=localhost
DB_DATABASE=homestead
DB_USERNAME=homestead
DB_PASSWORD=secret
我们可以离开DB_HOST
线原样,但会使用下面的任务,这非常类似于我们在前面的教程用于设置任务更新其他三个APP_ENV
和APP_DEBUG
。
- name: set DB_DATABASE
lineinfile: dest=/var/www/laravel/.env regexp='^DB_DATABASE=' line=DB_DATABASE=laravel
- name: set DB_USERNAME
lineinfile: dest=/var/www/laravel/.env regexp='^DB_USERNAME=' line=DB_USERNAME=laravel
- name: set DB_PASSWORD
lineinfile: dest=/var/www/laravel/.env regexp='^DB_PASSWORD=' line=DB_PASSWORD={{ dbpwd.stdout }}
when: dbpwd.changed
当我们与MySQL用户创建的任务做了,我们已经使用了生成的密码变量( dbpwd.stdout
)来用密码的文件,并添加了when
选项,以确保当只运行dbpwd
发生了变化。
现在,因为.env
之前,我们增加了我们的密码生成任务文件已经存在,我们需要将密码保存到另一个文件。 生成任务可以查找该文件的存在(我们已在任务中设置)。 我们也将使用sudo
和sudo_user
选项告诉Ansible创建文件作为www-data
的用户。
- name: Save dbpw file
lineinfile: dest=/var/www/laravel/.dbpw line="{{ dbpwd.stdout }}" create=yes state=present
sudo: yes
sudo_user: www-data
when: dbpwd.changed
打开php.yml
文件进行编辑。
nano php.yml
将以上任务添加到剧本中; 文件的结尾应该匹配以下。
. . .
- name: Create MySQL User
mysql_user: name=laravel password={{ dbpwd.stdout }} priv=laravel.*:ALL state=present
when: dbpwd.changed
- name: set DB_DATABASE
lineinfile: dest=/var/www/laravel/.env regexp='^DB_DATABASE=' line=DB_DATABASE=laravel
- name: set DB_USERNAME
lineinfile: dest=/var/www/laravel/.env regexp='^DB_USERNAME=' line=DB_USERNAME=laravel
- name: set DB_PASSWORD
lineinfile: dest=/var/www/laravel/.env regexp='^DB_PASSWORD=' line=DB_PASSWORD={{ dbpwd.stdout }}
when: dbpwd.changed
- name: Save dbpw file
lineinfile: dest=/var/www/laravel/.dbpw line="{{ dbpwd.stdout }}" create=yes state=present
sudo: yes
sudo_user: www-data
when: dbpwd.changed
handlers:
. . .
再次,不运行的剧本呢!我们有一个步骤完成之前,我们可以运行的剧本。
第7步 - 迁移数据库
在这一步中,我们将运行数据库迁移来设置数据库表。
在Laravel,这是通过运行完成migrate
命令(即php artisan migrate --force
的Laravel目录内)。 请注意,我们已经加入了--force
标志,因为production
环境需要它。
执行这个的Ansible任务看起来像这样。
- name: Run artisan migrate
shell: php /var/www/laravel/artisan migrate --force
sudo: yes
sudo_user: www-data
when: dbpwd.changed
现在是时候更新我们的剧本。 打开php.yml
文件进行编辑。
nano php.yml
将以上任务添加到剧本中; 文件的结尾应该匹配以下。
. . .
- name: Save dbpw file
lineinfile: dest=/var/www/laravel/.dbpw line="{{ dbpwd.stdout }}" create=yes state=present
sudo: yes
sudo_user: www-data
when: dbpwd.changed
- name: Run artisan migrate
shell: php /var/www/laravel/artisan migrate --force
sudo: yes
sudo_user: www-data
when: dbpwd.changed
handlers:
. . .
最后,我们可以保存和运行剧本。
ansible-playbook php.yml --ask-sudo-pass
完成执行后,在浏览器中刷新页面,您应该会看到一条消息:
Queue: NO
Cron: NO
这意味着数据库设置正确并按预期工作,但我们尚未设置cron任务或队列守护程序。
第8步 - 配置cron任务
在此步骤中,我们将设置任何需要配置的cron任务。
Cron任务是按照设置的计划运行的命令,可用于为应用程序执行任意数量的任务,例如执行维护任务或发送电子邮件活动更新 - 基本上任何需要定期执行的操作,而无需手动用户干预。 Cron任务可以每分钟运行一次,或者根据需要频繁运行。
Laravel自带的工匠命令调用schedule:run
在默认情况下,它被设计为每分钟运行,在应用程序中执行规定的计划任务。 这意味着如果我们的应用程序利用了这个特性,我们只需要添加一个cron任务。
Ansible具有cron
与多个直接转换成可以通过cron配置的不同选择不同的选项模块:
-
job
:要执行的命令。 如果state = present,则为必需。 -
minute
,hour
,day
,month
,和weekday
:分,小时,天,月,或当作业应当运行,分别是星期几。 -
special_time
(reboot
,yearly
,annually
,monthly
,weekly
,daily
,hourly
):特殊时间规范昵称。
默认情况下,它将创建一个每分钟运行的任务,这是我们想要的。 这意味着我们想要的任务看起来像这样:
- name: Laravel Scheduler
cron: >
job="run-one php /var/www/laravel/artisan schedule:run 1>> /dev/null 2>&1"
state=present
user=www-data
name="php artisan schedule:run"
在run-one
命令是在Ubuntu确保该命令只被运行一次的小帮手。 这意味着,如果先前的schedule:run
命令仍在运行,它不会被再次运行。 这有助于避免cron任务变为锁定在循环中的情况,并且随着时间的推移,在服务器用完资源之前,开始执行相同任务的越来越多的实例。
和以前一样,打开php.yml
文件进行编辑。
nano php.yml
将上述任务添加到剧本中; 文件的结尾应该匹配以下。
. . .
- name: Run artisan migrate
shell: php /var/www/laravel/artisan migrate --force
sudo: yes
sudo_user: www-data
when: dbpwd.changed
- name: Laravel Scheduler
cron: >
job="run-one php /var/www/laravel/artisan schedule:run 1>> /dev/null 2>&1"
state=present
user=www-data
name="php artisan schedule:run"
handlers:
. . .
保存并运行剧本:
ansible-playbook php.yml --ask-sudo-pass
现在,在浏览器中刷新页面。 在一分钟内,它会更新为这样。
Queue: NO
Cron: YES
这意味着cron正确地在后台工作。 作为示例应用程序的一部分,有一个cron作业,每分钟更新一个状态条目在数据库中,所以应用程序知道它正在运行。
第9步 - 配置队列守护程序
像schedule:run
第8步工匠命令,Laravel还带有可与启动队列工作queue:work --daemon
工匠命令。 在这一步中,我们将为Laravel配置队列守护进程worker。
队列工作者与cron作业相似,因为它们在后台运行任务。 区别在于应用程序通过用户执行的操作或通过cron作业调度的任务将作业推送到队列中。 队列任务由工作者一次执行一个,并且当它们在队列中被找到时将被按需处理。 队列任务通常用于需要时间执行的工作,例如发送电子邮件或对外部服务进行API调用。
不同的是schedule:run
命令,这不是一个需要每分钟运行一次的命令。 相反,它需要作为守护进程在后台不断运行。 要做到这一点的常用方法是使用第三方的包像supervisord,但是该方法需要了解如何配置和管理该系统。 还有一个更简单的使用cron和在实现它的方式run-one
命令。
我们将创建一个cron项启动队列工作守护进程,并使用run-one
来运行它。 这意味着它们的cron将启动过程中,它运行在第一时间,和任何后续的cron运行将由忽略run-one
工人正在运行时。 只要工人停止, run-one
将允许命令再次运行,并且队列工人将再次启动。 它是一个令人难以置信的简单和易于使用的方法,可以帮助您不需要学习如何配置和使用其他工具。
考虑到所有这些,我们将创建另一个cron任务来运行队列worker。
- name: Laravel Queue Worker
cron: >
job="run-one php /var/www/laravel/artisan queue:work --daemon --sleep=30 --delay=60 --tries=3 1>> /dev/null 2>&1"
state=present
user=www-data
name="Laravel Queue Worker"
和以前一样,打开php.yml
文件进行编辑。
nano php.yml
将上述任务添加到剧本中; 文件的结尾应该匹配以下内容:
. . .
- name: Laravel Scheduler
cron: >
job="run-one php /var/www/laravel/artisan schedule:run 1>> /dev/null 2>&1"
state=present
user=www-data
name="php artisan schedule:run"
- name: Laravel Queue Worker
cron: >
job="run-one php /var/www/laravel/artisan queue:work --daemon --sleep=30 --delay=60 --tries=3 1>> /dev/null 2>&1"
state=present
user=www-data
name="Laravel Queue Worker"
handlers:
. . .
保存并运行剧本:
ansible-playbook php.yml --ask-sudo-pass
与之前一样,在浏览器中刷新页面。 一分钟后,它会更新为如下所示:
Queue: YES
Cron: YES
这意味着队列worker正确地在后台工作。 我们在上一步中启动的cron作业将作业推送到队列中。 此作业在数据库运行时更新数据库,以显示其正在工作。
我们现在有一个工作示例Laravel应用程序,其中包括功能cron作业和队列工人。
结论
本教程涵盖了使用Ansible部署PHP应用程序时的一些更高级的主题。 所有使用的任务可以很容易地修改,以适应大多数PHP应用程序(取决于他们的具体要求),它应该给你一个良好的起点,为您的应用程序设置自己的剧本。
我们没有使用一个单一的SSH命令,本教程中(除了检查的一部分www-data
的用户登录),和一切-包括MySQL用户密码-已自动成立。 遵循本教程后,您的应用程序已准备就绪,并支持推送代码更新的工具。