If you liked what you've learned so far, dive in!
Subscribe to get access to this tutorial plus
video, code and script downloads.
PHP, MySQL and Nginx: check, check and check! But we've only installed php7.1-cli
.
An in reality, we need a lot more than that! What about php7.1-mysql
or php7.1-fpm
?
Yep, we need those friendly extensions... and a few others.
Wonderfully, on Ubuntu, these are all installed via apt-get
. We could copy and
paste the php7.1-cli
task over and over and over again for each package. Or,
to level-up our Ansible-awesomeness, we can loop!
Let's see how: change the task's name to Install PHP packages
:
- hosts: vb | |
tasks: | |
... lines 5 - 44 | |
- name: Install PHP packages | |
... lines 46 - 56 |
Then, instead of php7.1-cli
, add the very cryptic "{{ item }}"
:
- hosts: vb | |
tasks: | |
... lines 5 - 44 | |
- name: Install PHP packages | |
... line 46 | |
apt: | |
name: "{{ item }}" | |
... lines 49 - 56 |
Finish it with a new with_items
key after the apt
module. This gets a big
list of the stuff we want: php7.1-cli
, php7.1-curl
, ice cream, php7.1 -fpm
,
php7.1-intl
, a pony and php7.1-mysql
:
- hosts: vb | |
tasks: | |
... lines 5 - 44 | |
- name: Install PHP packages | |
become: true | |
apt: | |
name: "{{ item }}" | |
state: latest | |
with_items: | |
- php7.1-cli | |
- php7.1-curl | |
- php7.1-fpm | |
- php7.1-intl | |
- php7.1-mysql |
Tip
Using a loop in apt
module is deprecated and will be removed in version 2.11.
Instead of using the loop and specifying name: {{ item }}
, you can pass an array
to the name
key and specify the items like this:
---
- hosts: vb
tasks:
# ...
- name: Install PHP packages
become: true
apt:
name:
- php7.1-cli
- php7.1-curl
- php7.1-fpm
- php7.1-intl
- php7.1-mysql
state: latest
If we need more goodies later, we can add them. Flip over to your terminal and try it!
ansible-playbook ansible/playbook.yml -i ansible/hosts.ini
While we wait, let's check out the code we just wrote. For the first time, we're
seeing Ansible's templating language in action! Yep, whenever you see {{ }}
, you're
writing Jinja code - a Python templating language... which - guess what - is
more or less identical to Twig. Win!
In this case, we're opening up Jinja to print a variable called item
. That works
because Ansible
has this nice with_items
loop feature. And notice, this is not
special to the apt
module - it'll work anywhere.
Oh, and those quotes are important:
- hosts: vb | |
tasks: | |
... lines 5 - 44 | |
- name: Install PHP packages | |
... line 46 | |
apt: | |
name: "{{ item }}" | |
... lines 49 - 56 |
Quoting is usually optional in YAML. But if a value starts with {{
, it's mandatory.
Head back to the terminal. Yes! Celebrate! PHP extensions installed! I'll move to
my third tab where I've already run vagrant ssh
to get into the VM. Check for the
MySQL extension:
php -i | grep mysql
See that PDO MySQL stuff? That proves it worked!
Re-run that command again and look for timezone
:
php -i | grep timezone
php.ini
settings with lineinfileHmm, it says date.timezone
no value, which means that it is not set in php.ini
.
Since PHP 7.0, that's not a huge deal - in PHP 5 this caused an annoying warning.
But, I still want to make sure it's set.
Question number 1 is... where the heck is my php.ini
file? Answer, run:
php --ini
There it is /etc/php/7.1/cli/php.ini
. Open that up in vim
and hit /timezone
,
enter, to find that setting:
[Date]
; Defines the default timezone used by the date functions
; http://php.net/date.timezone
;date.timezone =
Ok, it's commented-out right now. We want Ansible to uncomment that line and set it
to UTC. Quit with Escape, :q
, enter.
So how can we tell Ansible to make a change right in the middle of a file? Of course, Ansible has a module just for that! Search for the "Ansible lineinfile" module. Ah, ha!
Ensure a particular line is in a file, or replace an existing line
Let's check out the options! The only required one is path
- the file we need
to change. Then, we can use the regexp
option to find the target line and line
as the value to replace it with.
Before we do this, look back at the path
option. It says that before Ansible
2.3, this was called dest
, destfile
or name
instead of path
. What version
do we have? Find out:
ansible --version
We're on 2.1! So instead of path
, we need to use dest
. This is something to
watch out for... because at the time of this recording, Ansible 2.3 isn't even
released yet! For some reason, Ansible always shows the docs for its latest, unreleased
version.
Let's rock! Add the new task: Set date.timezone for CLI
. Add become: true
and
use the lineinfile
module:
- hosts: vb | |
tasks: | |
... lines 5 - 56 | |
- name: Set date.timezone for CLI | |
become: true | |
lineinfile: | |
... lines 60 - 70 |
For options, pass it dest: /etc/php/7.1/cli/php.ini
and regexp: date.timezone =
:
- hosts: vb | |
tasks: | |
... lines 5 - 56 | |
- name: Set date.timezone for CLI | |
become: true | |
lineinfile: | |
dest: /etc/php/7.1/cli/php.ini | |
regexp: "date.timezone =" | |
... lines 62 - 70 |
We're not leveraging any regex here: this will simply find a line that contains date.timezone =
.
Finally, add line: date.timezone = UTC
:
- hosts: vb | |
tasks: | |
... lines 5 - 56 | |
- name: Set date.timezone for CLI | |
become: true | |
lineinfile: | |
dest: /etc/php/7.1/cli/php.ini | |
regexp: "date.timezone =" | |
line: "date.timezone = UTC" | |
... lines 63 - 70 |
With the line
option, the entire line will be replaced - not just the part that
was matched from the regexp
option. That means the comment at the beginning of the
line will be removed.
Tip
There is also an ini_file
module, which makes modifying .ini
files even easier.
For an example, see: http://bit.ly/knpu-ini-module
Now, copy that entire task and paste it. In Ubuntu, there are 2 different php.ini
files: rename this one to Set date.timezone for FPM
. Change the dest
path
from cli/
to fpm/
. That's the correct path inside the VM:
- hosts: vb | |
tasks: | |
... lines 5 - 63 | |
- name: Set date.timezone for FPM | |
become: true | |
lineinfile: | |
dest: /etc/php/7.1/fpm/php.ini | |
regexp: "date.timezone =" | |
line: "date.timezone = UTC" |
Run it!
ansible-playbook ansible/playbook.yml -i ansible/hosts.ini
Before that finishes, flip back to the VM and check the timezone setting:
php -i | grep timezone
No value. Then... once it finishes... try it again:
php -i | grep timezone
Got it! UTC! I'll open up my php.ini
to be sure... and...
[Date]
; Defines the default timezone used by the date functions
; http://php.net/date.timezone
date.timezone = UTC
Yes! The line was perfectly replaced.
Say hello to lineinfile
: your Swiss army knife for updating configuration files.
Hey Ho,
thanks for the php.ini line replacement. But wouldn't it be better to use:
http://docs.ansible.com/ansible/latest/ini_file_module.html
Like:
- name: Set date.timezone for PHP
become: true
ini_file:
dest: /etc/php/7.0/fpm/php.ini
section: Date
option: "{{ item.option }}"
value: "{{ item.value }}"
with_items:
- { option: date.timezone, value: Europe/Berlin }
- { option: max_execution_time, value: 60 }
- { option: memory_limit, value: 512M }
And what if I want to set it to PHP FPM and CLI? More Irems? Some Python way I don't know? One more use of ini_file?
Cheers Karsten
Okay my last entry is sadly wrong. Every setting than goes to the "Date" section...
- name: Set date.timezone for PHP
become: true
ini_file:
dest: /etc/php/7.0/fpm/php.ini
section: "{{ item.section }}"
option: "{{ item.option }}"
value: "{{ item.value }}"
with_items:
- { section: Date, option: date.timezone, value: Europe/Berlin }
- { section: PHP, option: max_execution_time, value: 60 }
- { section: PHP, option: memory_limit, value: 512M }
Hey Karsten,
Good question! Actually, Id say it would be better. But why didn't we use it in our screencasts? Well, the simplest answer is because... I see this module for the first time :p So, I think we'll add a note about this module, but you're right, if there's a specific module for some kind of tweaks - better to use that specific module instead of re-inventing the wheel ;)
Thanks for sharing it with us!
Cheers!
What about setting it for both FPM and CLI - take a look at this trick:
- name: Set date.timezone for PHP
become: true
ini_file:
dest: "/etc/php/7.0/{{ item.env }}/php.ini"
section: Date
option: "{{ item.option }}"
value: "{{ item.value }}"
with_items:
- { env: fpm, option: date.timezone, value: Europe/Berlin }
- { env: cli, option: date.timezone, value: Europe/Berlin }
- { env: fpm, option: max_execution_time, value: 60 }
- { env: cli, option: max_execution_time, value: 60 }
# ...
This is the most simple solution of doing it in a single task I think. Or, yes, use another one task for CLI.
Cheers!
// composer.json
{
"require": {
"php": ">=5.5.9",
"symfony/symfony": "3.1.*", // v3.1.4
"doctrine/orm": "^2.5", // v2.7.2
"doctrine/doctrine-bundle": "^1.6", // 1.6.4
"doctrine/doctrine-cache-bundle": "^1.2", // 1.3.0
"symfony/swiftmailer-bundle": "^2.3", // v2.3.11
"symfony/monolog-bundle": "^2.8", // 2.11.1
"symfony/polyfill-apcu": "^1.0", // v1.2.0
"sensio/distribution-bundle": "^5.0", // v5.0.12
"sensio/framework-extra-bundle": "^3.0.2", // v3.0.16
"incenteev/composer-parameter-handler": "^2.0", // v2.1.2
"doctrine/doctrine-migrations-bundle": "^1.2", // v1.2.0
"snc/redis-bundle": "^2.0", // 2.0.0
"predis/predis": "^1.1", // v1.1.1
"composer/package-versions-deprecated": "^1.11" // 1.11.99
},
"require-dev": {
"sensio/generator-bundle": "^3.0", // v3.0.8
"symfony/phpunit-bridge": "^3.0", // v3.1.4
"doctrine/data-fixtures": "^1.1", // 1.3.3
"hautelook/alice-bundle": "^1.3" // v1.4.1
}
}
<b>PSA</b>: using ansible 2.7.0 produces: