介绍
在本教程中,我们将介绍如何设置SSH密钥以支持代码部署/发布工具,配置系统防火墙,配置和配置数据库(包括密码!),以及设置任务调度程序(crons)和队列守护进程。本教程结束时的目标是让您拥有一个具有上述高级配置的完全可用的PHP应用程序服务器。
与上一个教程一样,我们将使用Laravel框架作为示例PHP应用程序。但是,如果您已拥有自己的框架和应用程序,则可以轻松修改这些说明以支持其他框架和应用程序。
在本教程中,我们将使用Ansible在Ubuntu 14.04 Droplet上安装和配置Nginx,PHP和其他服务。本教程以Ansible的基本知识为基础。
要学习本教程,您需要:
your_server_ip
IP地址。sudo
命令的非root账号的Ubuntu服务器,并且已开启防火墙。没有服务器的同学可以在这里购买,不过我个人更推荐您使用免费的腾讯云开发者实验室进行试验,学会安装后再购买服务器。)在此步骤中,我们将Git存储库更新为稍微自定义的示例存储库。
由于默认的Laravel安装不需要我们将在本教程中设置的高级功能,因此我们将现有存储库从标准存储库切换到添加了一些调试代码的示例存储库,只是为了显示何时工作正常。我们将使用的存储库位于https://github.com/do-community/do-ansible-adv-php
。
如果您还没有这样做,请将目录更改为ansible-php
。
cd ~/ansible-php/
打开我们现有的剧本进行编辑。
nano php.yml
查找并更新“Clone git repository”任务,看起来像这样。
- 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/
)。您应该看到一条消息“无法找到驱动程序”。
这意味着我们已成功换出示例存储库的默认存储库,但应用程序无法连接到数据库。这是我们期望在这里看到的,我们将在本教程的后面安装和设置数据库。
在此步骤中,我们将设置可用于应用程序代码部署脚本的SSH密钥。
虽然Ansible非常适合维护配置和设置服务器和应用程序,但Envoy和Rocketeer等工具通常用于将代码更改推送到服务器并远程运行应用程序命令。大多数这些工具都需要SSH连接才能直接访问应用程序安装。在我们的例子中,这意味着我们需要为www-data
用户配置SSH密钥。
我们将需要您希望从中推送代码的用户的公钥文件。此文件通常位于~/.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
模块完成,并将shell设置为/bin/bash
(或您首选的shell)。
- name: Set www-data user shell
user: name=www-data shell=/bin/bash
现在,打开剧本进行编辑以添加新任务。
nano php.yml
将上述任务添加到您的php.yml
剧本中; 文件的末尾应与以下内容匹配。添加内容为后2段以“-”开头的文字。
. . .
- 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完成时,您应该能够使用该www-data
用户来SSH 。
ssh www-data@your_server_ip
如果您成功登录,它正在运行!您现在可以通过输入logout
或按CTRL + D进行注销。
我们不需要将该连接用于本教程中的任何其他步骤,但是如果您正在设置其他工具(如上所述)或者根据需要进行常规调试和应用程序维护,它将非常有用。
在此步骤中,我们将在服务器上配置防火墙,以仅允许HTTP和SSH连接。
Ubuntu 14.04 默认安装了UFW(Uncomplicated Firewall),Ansible支持该ufw
模块。它具有许多强大的功能,并且设计得尽可能简单。它非常适合只需要打开几个端口的自包含Web服务器。在我们的例子中,我们希望打开端口80(HTTP)和端口22(SSH)。您可能还需要端口443用于HTTPS。
该ufw
模块具有许多不同的选项,可执行不同的任务。我们需要执行的不同任务是:
这可以分别通过以下任务完成。
新的Ansible任务
- 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
选项。它应该返回这样的东西。
UFW状态输出
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)
在这一步中,我们将为我们的应用程序设置一个MySQL数据库。
(自建服务器难免会遇到这样的问题,配置SSL很麻烦,虽然对一部分人来说这也是一种乐趣,但是如果您在生产环境使用,我还是建议您直接使用云关系型数据库,云关系型数据库让您在云中轻松部署、管理和扩展的关系型数据库,提供安全可靠、伸缩灵活的按需云数据库服务。腾讯云关系型数据库提供 MySQL、SQL Server、MariaDB、PostgreSQL 数据库引擎,并针对数据库引擎的性能进行了优化。云关系型数据库是一种高度可用的托管服务,提供容灾、备份、恢复、监控、迁移等数据库运维全套解决方案,可将您从耗时的数据库管理任务中解放出来,让您有更多时间专注于您的应用和业务。)
第一步是确保在我们的服务器上安装MySQL,只需将所需的包添加到我们的playbook顶部的安装包任务即可。我们需要的包有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
在这一步中,我们将为我们的应用程序创建一个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中使用任务时,记住在运行任务之前完成处理任务输出/结果的整个工作流程以避免必须手动登录和重置状态,这一点非常重要。
在此步骤中,我们将MySQL数据库密码保存到.env
应用程序的文件中。
就像我们在上一个教程中所做的那样,我们将更新.env
文件以包含我们新创建的数据库凭据。默认情况下,Laravel的.env
文件包含以下行:
DB_HOST=localhost
DB_DATABASE=homestead
DB_USERNAME=homestead
DB_PASSWORD=secret
我们可以按DB_HOST
原样保留该行,但是将使用以下任务更新其他三行:
- 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:
. . .
再次,不要运行剧本了!在我们运行剧本之前,我们还有一个步骤要完成。
在此步骤中,我们将运行数据库迁移以设置数据库表。
在Laravel中,这是通过在Laravel目录中运行migrate
命令(即php artisan migrate --force
)来完成的。请注意,我们添加了--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任务或队列守护程序。
在此步骤中,我们将设置需要配置的任何cron任务。
Cron任务是在设定的时间表上运行的命令,可用于为您的应用程序执行任意数量的任务,例如执行维护任务或发送电子邮件活动更新 - 基本上任何需要定期完成而无需手动用户干预的任务。Cron任务可以像每分钟一样频繁地运行,也可以根据需要不频繁地运行。
Laravel附带默认调用的名为schedule:run
的Artisan命令,该命令旨在每分钟运行并在应用程序中执行定义的计划任务。这意味着如果我们的应用程序利用此功能,我们只需要添加一个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作业每分钟运行一次,更新数据库中的状态条目,以便应用程序知道它正在运行。
与步骤8中的名为schedule:run
的Artisan命令一样,Laravel还带有一个可以使用queue:work --daemon
Artisan命令启动的队列工作程序。在此步骤中,我们将为Laravel配置队列守护程序worker。
队列工作者与cron作业类似,因为他们在后台运行任务。不同之处在于应用程序通过用户执行的操作或通过cron作业调度的任务将作业推送到队列中。队列任务由工作者一次执行一次,并且当在队列中找到它们时将按需处理。队列任务通常用于需要时间执行的工作,例如发送电子邮件或对外部服务进行API调用。
与schedule:run
命令不同,这不是每分钟都需要运行的命令。相反,它需要不断在后台运行作为守护进程。一种常见的方法是使用像supervisord这样的第三方软件包,但该方法需要了解如何配置和管理所述系统。使用cron和run-one
命令实现它的方法要简单得多。
我们将创建一个cron条目来启动队列工作守护程序,并用run-one
它来运行它。这意味着cron将在第一次运行时启动该进程,并且当工作程序运行时,任何后续的cron运行都将被忽略run-one
。一旦工作程序停止,run-one
将允许该命令再次运行,并且队列工作程序将再次启动。这是一种非常简单易用的方法,使您无需学习如何配置和使用其他工具。
考虑到所有这些,我们将创建另一个cron任务来运行我们的队列工作者。
- 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
这意味着队列工作者正在后台正常工作。我们在上一步中启动的cron作业将作业推送到队列中。此作业在运行时更新数据库以显示其正在运行。
我们现在有一个工作示例Laravel应用程序,其中包括正常运行的cron作业和队列工作程序。
本教程介绍了使用Ansible部署PHP应用程序时的一些更高级的主题。所有使用的任务都可以轻松修改,以适应大多数PHP应用程序(取决于他们的具体要求),它应该为您的应用程序设置自己的Playbooks提供一个很好的起点。
我们没有使用单个SSH命令作为本教程的一部分(除了检查www-data
用户登录之外),并且所有内容(包括MySQL用户密码)都已自动设置。完成本教程后,您的应用程序已准备就绪,并支持推送代码更新的工具。
更多Ubuntu教程请前往腾讯云+社区学习更多知识。
参考文献:《How To Deploy an Advanced PHP Application Using Ansible on Ubuntu 14.04》
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
扫码关注腾讯云开发者
领取腾讯云代金券
Copyright © 2013 - 2025 Tencent Cloud. All Rights Reserved. 腾讯云 版权所有
深圳市腾讯计算机系统有限公司 ICP备案/许可证号:粤B2-20090059 深公网安备号 44030502008569
腾讯云计算(北京)有限责任公司 京ICP证150476号 | 京ICP备11018762号 | 京公网安备号11010802020287
Copyright © 2013 - 2025 Tencent Cloud.
All Rights Reserved. 腾讯云 版权所有