本教程是关于在Ubuntu 14.04上使用Ansible部署PHP应用程序的系列文章中的第三篇。第一个教程涵盖了部署应用程序的基本步骤; 在第二个教程介绍更高级的主题,如数据库,队列守护进程和任务调度(crons)。
在本教程中,我们将基于我们在前面的教程中学到的内容,将我们的单应用程序Ansible playbook转换为支持在一个或多个服务器上部署多个PHP应用程序的playbook。在使用Ansible以最小的努力部署应用程序时,这是最后一块拼图。
我们将使用几个简单的Lumen应用程序作为我们示例的一部分。但是,如果您已拥有自己的框架和应用程序,则可以轻松修改这些说明以支持其他框架和应用程序。建议您使用示例应用程序,直到您可以轻松地对剧本进行更改。
要学习本教程,您需要:
your_first_server_ip
和your_second_server_ip
。/etc/hosts
中,添加以下行。您可以在本教程的第6步中了解有关此文件的更多信息。your_first_server_ip laravel.example.com one.example.com two.example.com
your_second_server_ip laravel.example2.com two.example2.com
我们将在本教程中使用的示例网站是laravel.example.com
,one.example.com
和two.example.com
。
在这一步中,我们将设置playbook变量来定义我们的新应用程序。
在之前的教程中,我们对所有配置细节进行了硬编码,这对于执行特定应用程序特定任务的许多剧本来说是正常的。但是,当您希望支持多个应用程序或扩大您的剧本范围时,将所有内容硬编码就都不再具有意义。
正如我们之前看到的,Ansible提供了可以在任务定义和文件模板中使用的变量。我们还没有看到的是如何手动设置变量。在你的剧本的顶部,除了旁边的hosts
和tasks
参数,你还可以自己定义一个vars
参数,并在那里设置变量。
如果您还没有这样做,请从以前的教程中学习并将目录更改为ansible-php
。
cd ~/ansible-php/
打开我们现有的剧本进行编辑。
nano php.yml
文件顶部应如下所示:
---
- hosts: php
sudo: yes
tasks:
. . .
要定义变量,我们可以就在在hosts
,sudo
和tasks
的旁边添加一个vars
部分。为了简单起见,我们将从一个非常基本用户名变量www-data
开始,如下所示:
---
- hosts: php
sudo: yes
vars:
wwwuser: www-data
tasks:
. . .
接下来,检查并使用新变量{{ wwwuser }}
将所有出现的www-data
用户更新。这种格式您应该很熟悉,因为我们已经在外观和查找中使用了它。
要查找和替换使用nano,请按CTRL+\
。你会看到一个提示,这表示搜索(更换)。输入www-data,然后按ENTER
。这时提示会更改为替换为:。在此处输入{{wwwuser}}并再按ENTER
一次。Nano将带您通过www-data
的每个实例并询问替换此实例?。您可以按y
来逐个替换每个,或者按a
来进行全部替换。
注意:确保我们刚刚添加到顶部的变量声明也不会更改。应该有11个需要更换的www-data
实例。
在我们进一步讨论之前,当涉及变量时,有一些事项需要我们注意。当它们在更长的行内时,我们通常可以像这样添加它们:
- name: create /var/www/ directory
file: dest=/var/www/ state=directory owner={{ wwwuser }} group={{ wwwuser }} mode=0700
但是,如果变量是字符串中唯一的值,我们需要将其包装在引号中,以便YAML解析器可以正确理解它:
- name: Run artisan migrate
shell: php /var/www/laravel/artisan migrate --force
sudo: yes
sudo_user: "{{ wwwuser }}"
when: dbpwd.changed
在你的剧本中,任何时候当你有sudo_user: {{ wwwuser }}
时,你都需要执行此步骤。您可以使用全局查找然后以同样的方式进行替换,来取代sudo_user:{{wwwuser}}与sudo_user: “{{wwwuser}}” 。这里应该有四行需要这种改变。
更改完所有内容后,保存并运行playbook:
ansible-playbook php.yml --ask-sudo-pass
当没有更改任务的时候,就意味着我们的wwwuser
变量正常工作。
在本节中,我们将介绍复杂配置选项的嵌套变量。
在上一步中,我们设置了一个基本变量。但是,也可以嵌套变量并定义变量列表。这提供了我们需要来定义我们希望在服务器上建立的站点列表的功能。
首先,让我们考虑一下我们在playbook中设置的现有git存储库:
- name: Clone git repository
git: >
dest=/var/www/laravel
repo=https://github.com/do-community/do-ansible-adv-php.git
update=yes
version=example
我们可以提取以下有用的信息:名称(目录),存储库,分支和域。因为我们正在设置多个应用程序,所以我们还需要一个域名来响应。在这里,我们将使用laravel.example.com
,但如果您有自己的域名,则可以替换它。
这导致我们可以为此应用程序定义以下四个变量:
name: laravel
repository: https://github.com/do-community/do-ansible-adv-php.git
branch: example
domain: laravel.example.com
现在,打开你的剧本进行编辑:
nano php.yml
在顶部vars
部分,我们可以将我们的应用程序添加到新的应用程序列表中:
更新了php.yml中的应用程序变量
---
- hosts: php
sudo: yes
vars:
wwwuser: www-data
applications:
- name: laravel
domain: laravel.example.com
repository: https://github.com/do-community/do-ansible-adv-php.git
branch: example
...
如果你现在运行你的剧本(使用ansible-playbook php.yml --ask-sudo-pass
),并没有什么会发生改变,因为我们还没有设置我们的任务来使用我们的新applications
变量。但是,如果您在浏览器中访问http://laravel.example.com/
,它应该显示我们的原始应用程序。
在本节中,我们将学习如何遍历任务中的变量列表。
如前所述,变量列表需要在我们希望使用它们的每个任务中循环。正如我们在install packages
任务中看到的那样,我们需要定义一个项循环,然后为列表中的每个项应用任务。
打开你的剧本进行编辑:
nano php.yml
我们先从一些简单的任务开始。在游戏手册的中间,您应该找到以下两个env
任务:
- 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
您会注意到它们目前使用该laravel
目录进行了硬编码。我们想要更新它以使用每个应用程序的name
属性。为此,我们添加with_items
选项以循环我们的applications
列表。在任务本身内,我们将换出变量{{ item.name }}
的laravel
引用,这应该是和我们以前使用过的格式很类似的。
它应该如下所示:
- name: set APP_DEBUG=false
lineinfile: dest=/var/www/{{ item.name }}/.env regexp='^APP_DEBUG=' line=APP_DEBUG=false
with_items: applications
- name: set APP_ENV=production
lineinfile: dest=/var/www/{{ item.name }}/.env regexp='^APP_ENV=' line=APP_ENV=production
with_items: applications
接下来,向下移动两个Laravel artisan cron任务。它们的更新方式与我们刚刚完成的env
任务完全相同。我们还将添加item.name
到cron条目的name
参数中,因为Ansible使用此字段来唯一标识每个cron条目。如果我们按原样离开它们,我们将无法在同一服务器上拥有多个站点,因为它们会不断地覆盖每个站点,最终只会保存最后一个站点。
任务应如下所示:
- name: Laravel Scheduler
cron: >
job="run-one php /var/www/{{ item.name }}/artisan schedule:run 1>> /dev/null 2>&1"
state=present
user={{ wwwuser }}
name="{{ item.name }} php artisan schedule:run"
with_items: applications
- name: Laravel Queue Worker
cron: >
job="run-one php /var/www/{{ item.name }}/artisan queue:work --daemon --sleep=30 --delay=60 --tries=3 1>> /dev/null 2>&1"
state=present
user={{ wwwuser }}
name="{{ item.name }} Laravel Queue Worker"
with_items: applications
如果您现在保存并运行该剧本(使用ansible-playbook php.yml --ask-sudo-pass
),您应该只看到两个更新的cron
任务被更新。这是由于name
参数的变化。除此之外,没有任何变化,这意味着我们的应用程序列表正在按预期工作,我们还没有通过重构我们的playbook对我们的服务器进行任何更改。
在本节中,我们将介绍如何在模板中使用循环变量。
模板中的循环变量非常简单。它们的使用方式与在任务中使用的方式完全相同,就像所有其他变量一样。当你考虑文件路径和变量时会出现复杂性,因为在某些用途中我们需要考虑文件名,甚至因为新文件而运行其他命令。
对于Nginx,我们需要为每个应用程序创建一个新的配置文件,并告诉Nginx应该启用它。我们还希望在此过程中删除原始配置文件/etc/nginx/sites-available/default
。
首先,打开你的剧本进行编辑:
nano php.yml
找到Configure Nginx
任务(靠近剧本中间),并像我们完成其他任务一样更新它:
- name: Configure nginx
template: src=nginx.conf dest=/etc/nginx/sites-available/{{ item.name }}
with_items: applications
notify:
- restart php5-fpm
- restart nginx
虽然我们已经到了这一步,但我们还将增加上面提到的两个任务。首先,我们将告诉Nginx我们的新站点配置文件。这是通过在/var/nginx/
中的sites-available
和sites-enabled
目录之间的符号链接来完成的。
在任务之后添加此Configure nginx
任务:
- name: Configure nginx symlink
file: src=/etc/nginx/sites-available/{{ item.name }} dest=/etc/nginx/sites-enabled/{{ item.name }} state=link
with_items: applications
notify:
- restart php5-fpm
- restart nginx
接下来,我们要删除已启用的站点配置文件default
,以免它导致我们的新站点配置文件出现问题。这可以通过file
模块轻松完成:
- name: Remove default nginx site
file: path=/etc/nginx/sites-enabled/default state=absent
notify:
- restart php5-fpm
- restart nginx
请注意,我们不需要循环applications
,因为我们正在寻找单个文件。
您的剧本中的Nginx块现在应该如下所示:
- name: Configure nginx
template: src=nginx.conf dest=/etc/nginx/sites-available/{{ item.name }}
with_items: applications
notify:
- restart php5-fpm
- restart nginx
- name: Configure nginx symlink
file: src=/etc/nginx/sites-available/{{ item.name }} dest=/etc/nginx/sites-enabled/{{ item.name }} state=link
with_items: applications
notify:
- restart php5-fpm
- restart nginx
- name: Remove default nginx site
file: path=/etc/nginx/sites-enabled/default state=absent
notify:
- restart php5-fpm
- restart nginx
保存您的剧本并打开nginx.conf
文件进行编辑:
nano nginx.conf
更新配置文件,使其使用我们的变量:
server {
listen 80 default_server;
listen [::]:80 default_server ipv6only=on;
root /var/www/{{ item.name }}/public;
index index.php index.html index.htm;
server_name {{ item.domain }};
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/{{ item.name }}/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;
}
}
但是,我们尚未完成。请注意顶部的default_server
?我们希望仅包含laravel
应用程序的内容,使其成为默认值。为此,我们可以使用基本的IF语句来检查item.name
是否等于laravel
,如果是,则显示default_server
。
它看起来像这样:
server {
listen 80{% if item.name == "laravel" %} default_server{% endif %};
listen [::]:80{% if item.name == "laravel" %} default_server ipv6only=on{% endif %};
相应地更新nginx.conf
并保存。
现在是时候运行我们的剧本了:
ansible-playbook php.yml --ask-sudo-pass
您应该注意到Nginx任务已标记为已更改。完成运行后,在浏览器中刷新站点,它应该显示与上一个教程末尾相同的内容:
Queue: YES
Cron: YES
在这一步中,我们将在任务中循环多个变量。
现在是时候解决一个更复杂的循环示例,特别是注册变量。为了支持不同的状态并防止任务不必要地运行,您将记住我们在克隆git存储库任务中使用的register: cloned
的方法是将cloned
变量注册成为任务的状态。然后,我们在以下任务中使用when: cloned|changed
来条件地触发任务。现在我们需要更新这些引用以支持应用程序循环。
首先,打开你的剧本进行编辑:
nano php.yml
往继续下看Clone git仓库任务:
- name: Clone git repository
git: >
dest=/var/www/laravel
repo=https://github.com/do-community/do-ansible-adv-php.git
update=yes
version=example
sudo: yes
sudo_user: "{{ wwwuser }}"
register: cloned
当我们在此任务中注册变量时,我们不需要执行任何我们尚未执行的操作:
- name: Clone git repository
git: >
dest=/var/www/{{ item.name }}
repo={{ item.repository }}
update=yes
version={{ item.branch }}
sudo: yes
sudo_user: "{{ wwwuser }}"
with_items: applications
register: cloned
现在,向下移动您的剧本,直到找到composer create-project
任务:
- name: composer create-project
composer: command=create-project working_dir=/var/www/laravel optimize_autoloader=no
sudo: yes
sudo_user: "{{ wwwuser }}"
when: cloned|changed
现在,我们需要通过将其更新为同时循环 applications
和cloned
。这是通过使用with_together
选项完成的,并将其传入applications
和cloned
。当with_together
循环通过两个变量时,通过使用item.#
来完成访问项,其中#
是定义的变量的索引。例如:
with_together:
- list_one
- list_two
item.0
可以参考list_one
,item.1
可以参考list_two
。
这意味着,对于applications
,我们可以通过item.0.name
的方式访问这些属性:。对于cloned
,我们需要从任务中传递结果,这些结果可以通过cloned.results
进行访问,然后我们可以检查它是否已通过item.1.changed
进行更改。
这意味着任务变为:
- name: composer create-project
composer: command=create-project working_dir=/var/www/{{ item.0.name }} optimize_autoloader=no
sudo: yes
sudo_user: "{{ wwwuser }}"
when: item.1.changed
with_together:
- applications
- cloned.results
现在保存并运行你的剧本:
ansible-playbook php.yml --ask-sudo-pass
此次运行应该没有任何变化。但是,我们现在有一个注册变量在循环中很好地工作。
在本节中,我们将了解更复杂的已注册变量和循环。
转换中最复杂的部分是处理我们用于MySQL数据库密码生成的注册变量。也就是说,在我们尚未涉及的这一步骤中我们不得不做更多的事情,我们只需要立即更新一些任务。
打开您的剧本进行编辑:
nano php.yml
找到MySQL任务,在我们的初始传递中,我们将添加基本变量,就像我们在之前的任务中所做的那样:
- name: Create MySQL DB
mysql_db: name={{ item.name }} state=present
with_items: applications
- name: Generate DB password
shell: makepasswd --chars=32
args:
creates: /var/www/{{ item.name }}/.dbpw
with_items: applications
register: dbpwd
- name: Create MySQL User
mysql_user: name={{ item.name }} password={{ dbpwd.stdout }} priv={{ item.name }}.*:ALL state=present
when: dbpwd.changed
- name: set DB_DATABASE
lineinfile: dest=/var/www/{{ item.name }}/.env regexp='^DB_DATABASE=' line=DB_DATABASE={{ item.name }}
with_items: applications
- name: set DB_USERNAME
lineinfile: dest=/var/www/{{ item.name }}/.env regexp='^DB_USERNAME=' line=DB_USERNAME={{ item.name }}
with_items: applications
- name: set DB_PASSWORD
lineinfile: dest=/var/www/{{ item.name }}/.env regexp='^DB_PASSWORD=' line=DB_PASSWORD={{ dbpwd.stdout }}
when: dbpwd.changed
- name: Save dbpw file
lineinfile: dest=/var/www/{{ item.name }}/.dbpw line="{{ dbpwd.stdout }}" create=yes state=present
sudo: yes
sudo_user: "{{ wwwuser }}"
when: dbpwd.changed
- name: Run artisan migrate
shell: php /var/www/{{ item.name }}/artisan migrate --force
sudo: yes
sudo_user: "{{ wwwuser }}"
when: dbpwd.changed
接下来我们将添加 with_together
,以便我们可以使用我们的数据库密码。对于我们的密码生成,我们需要循环dbpwd.results
,并且能够从item.1.stdout
中访问密码,因为我们将通过item.0
访问applications
。
我们可以相应地更新我们的剧本:
- name: Create MySQL DB
mysql_db: name={{ item.name }} state=present
with_items: applications
- name: Generate DB password
shell: makepasswd --chars=32
args:
creates: /var/www/{{ item.name }}/.dbpw
with_items: applications
register: dbpwd
- name: Create MySQL User
mysql_user: name={{ item.0.name }} password={{ item.1.stdout }} priv={{ item.0.name }}.*:ALL state=present
when: item.1.changed
with_together:
- applications
- dbpwd.results
- name: set DB_DATABASE
lineinfile: dest=/var/www/{{ item.name }}/.env regexp='^DB_DATABASE=' line=DB_DATABASE={{ item.name }}
with_items: applications
- name: set DB_USERNAME
lineinfile: dest=/var/www/{{ item.name }}/.env regexp='^DB_USERNAME=' line=DB_USERNAME={{ item.name }}
with_items: applications
- name: set DB_PASSWORD
lineinfile: dest=/var/www/{{ item.0.name }}/.env regexp='^DB_PASSWORD=' line=DB_PASSWORD={{ item.1.stdout }}
when: item.1.changed
with_together:
- applications
- dbpwd.results
- name: Save dbpw file
lineinfile: dest=/var/www/{{ item.0.name }}/.dbpw line="{{ item.1.stdout }}" create=yes state=present
sudo: yes
sudo_user: "{{ wwwuser }}"
when: item.1.changed
with_together:
- applications
- dbpwd.results
- name: Run artisan migrate
shell: php /var/www/{{ item.0.name }}/artisan migrate --force
sudo: yes
sudo_user: "{{ wwwuser }}"
when: item.1.changed
with_together:
- applications
- dbpwd.results
更新了Playbook后,保存并运行它:
ansible-playbook php.yml --ask-sudo-pass
尽管我们对我们的剧本进行了所有更改,但数据库任务应该没有变化。通过此步骤的更改,我们应该完成从单个应用程序手册到多个应用程序手册的转换。
在这一步中,我们将在我们的剧本中配置另外两个应用程序。
既然我们已经重构了我们的playbook从而可以使用变量来定义应用程序,那么向我们的服务器添加新应用程序的过程非常简单。只需将它们添加到applications
变量列表中即可。这就是Ansible变量的力量真正闪耀的地方。
打开您的剧本进行编辑:
nano php.yml
在顶部,在该vars
部分中,找到applications
块:
applications:
- name: laravel
domain: laravel.example.com
repository: https://github.com/do-community/do-ansible-adv-php.git
branch: example
现在再添加两个应用程序:
applications:
- name: laravel
domain: laravel.example.com
repository: https://github.com/do-community/do-ansible-adv-php.git
branch: example
- name: one
domain: one.example.com
repository: https://github.com/do-community/do-ansible-php-example-one.git
branch: master
- name: two
domain: two.example.com
repository: https://github.com/do-community/do-ansible-php-example-two.git
branch: master
保存你的剧本。
现在是时候运行你的剧本了:
ansible-playbook php.yml --ask-sudo-pass
当设计者设置新的应用程序时,此步骤可能需要一段时间。完成后,您会注意到一些任务被更改,如果您仔细查看,您会注意到每个循环项目都会被列出。首先,我们的原始应用程序应该说ok
或者skipped
,而新的两个应用程序应该说changed
。
更重要的是,如果您在Web浏览器中访问已配置站点的所有三个域,则应注意三个不同的网站。
第一个应该看起来很熟悉。另外两个应该显示:
This is example app one!
和
This is example app two!
有了它,我们只需更新我们的应用程序列表就可以部署两个新的Web应用程序。
在这一步中,我们将变量提取到宿主变量。
退一步来说,Playbook变量很好,但是如果我们想使用相同的playbook将不同的应用程序部署到不同的服务器上呢?我们可以对每个任务进行条件检查,以确定哪个服务器正在运行任务,或者我们可以使用主机变量。主变量就是它们听起来的样子:适用于特定主机的变量,而不是整个剧本中的所有主机。
主机变量可以在hosts
文件中内联定义,就像我们使用ansible_ssh_user
变量一样,或者可以在目录中的每个主机的专用host_vars
文件中被定义。
首先,在我们的hosts
文件和我们的剧本旁边创建一个新目录。叫做host_vars
目录:
mkdir host_vars
接下来,我们需要为主机创建一个文件。Ansible使用的约定是文件名与hosts
文件中的主机名匹配。因此,例如,如果您的hosts
文件如下所示:
[php]
your_first_server_ip ansible_ssh_user=sammy
然后你应该创建一个名为host_vars/your_first_server_ip
的文件。我们现在创建:
nano host_vars/your_first_server_ip
与我们的剧本一样,主机文件使用YAML进行格式化。这意味着我们可以将applications
列表复制到新的主机文件中,因此它看起来像这样:
---
applications:
- name: laravel
domain: laravel.example.com
repository: https://github.com/do-community/do-ansible-adv-php.git
branch: example
- name: one
domain: one.example.com
repository: https://github.com/do-community/do-ansible-php-example-one.git
branch: master
- name: two
domain: two.example.com
repository: https://github.com/do-community/do-ansible-php-example-two.git
branch: master
保存新的主机文件,然后打开您的剧本进行编辑:
nano php.yml
更新顶部以删除整个applications
部分:
---
- hosts: php
sudo: yes
vars:
wwwuser: www-data
tasks:
. . .
保存剧本,然后运行它:
ansible-playbook php.yml --ask-sudo-pass
即使我们已将变量从我们的playbook移动到我们的宿主文件,输出应该看起来完全相同,并且Ansible不应该报告这里有任何变化。正如您所看到的,host_vars
工作方式与vars
在剧本中的工作方式完全相同; 它们仅适用于主机。
host_vars
文件中定义的变量也可以在管理服务器的所有剧本中被访问,这对于常用选项和设置很有用。但是,请注意不要在不同的剧本中使用可能意味着不同内容的通用名称。
在此步骤中,我们将使用新的主机文件并在第二台服务器上部署应用程序。
首先,我们需要使用新主机更新我们的hosts
文件。打开它进行编辑:
nano hosts
并添加到您的新主机:
[php]
your_first_server_ip ansible_ssh_user=sammy
your_second_server_ip ansible_ssh_user=sammy
保存并关闭文件。
接下来,我们需要创建一个新的hosts文件,就像我们在第一个文件中所做的那样。
nano host_vars/your_second_server_ip
您可以选择一个或多个示例应用程序并将其添加到主机文件中。例如,如果要将原始示例和示例2部署到新服务器,可以使用:
---
applications:
- name: laravel
domain: laravel.example2.com
repository: https://github.com/do-community/do-ansible-adv-php.git
branch: example
- name: two
domain: two.example2.com
repository: https://github.com/do-community/do-ansible-php-example-two.git
branch: master
保存你的剧本。
最后我们可以运行我们的剧本:
ansible-playbook php.yml --ask-sudo-pass
Ansible需要一段时间才能运行,因为它是在第二台服务器上设置了所有内容。完成后,在浏览器中打开您选择的应用程序(我们在示例中使用了laravel.example2.com
two.example2.com
)并确认它们已正确设置。您应该看到为主机文件选择的特定应用程序,并且原始服务器应该没有更改。
本教程采用了功能齐全的单一应用程序手册,并将其转换为支持跨多个服务器的多个应用程序。结合前面教程中介绍的主题,您应该拥有编写完整的Playbook来部署应用程序所需的一切。根据之前的教程,我们仍然没有使用SSH直接登录到服务器。
一旦我们完成了剧本的结构,您就会注意到添加更多应用程序和另一台服务器是多么简单。这是Ansible的强大功能,也是它如此灵活易用的原因。
更多Ubuntu教程请前往腾讯云+社区学习更多知识。
参考文献:《How To Deploy Multiple PHP Applications using Ansible on Ubuntu 14.04》
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。