A very useful Chef feature is the ability to manage files right from the Chef code. Either plain files can be copied or dynamic files can be generated through templates. We'll leverage this feature to create an example PHP test file and dynamically generate Apache VirtualHosts for our LAMP server, so you'll know how to reuse it anywhere else.
To work through this recipe, you will need the following:
We'll manage two different kinds of files in two different ways: a static file and a dynamic file generated from a template, so the most common usage is covered.
Let's begin by creating a basic PHP file that will only display the phpinfo()
result. This is done using the simple file
resource with the file path as argument, giving its content inline. Other optional properties of the file
resource include ownership information or the file mode. Add a file
resource to the php/recipes/default.rb
recipe:
file '/var/www/html/phpinfo.php' do content '<?php phpinfo(); ?>' mode '0644' owner 'root' group 'root' end
Don't forget to bump the version in php/metadata.rb
:
version '0.2.0'
Upload the new cookbook from your workstation:
$ knife cookbook upload php
Deploy using the Chef client on the remote node:
$ sudo chef-client
If you now navigate to http://node-hostname/phpinfo.php, you'll see the PHP information displayed.
This is the most static way of shipping a plain file.
Let's now create a generic Apache virtual host to fully control what we'll do with our LAMP server, and not just live with the default configuration shipped with our Linux distribution. We want the website's root folder to be /var/www/<sitename>
and the configuration file will live under /etc/httpd/conf.d/<sitename>.conf
. We'll ship a sample HTML index file as well, to validate we're running the correct virtual host.
Start by generating a new recipe in the Apache cookbook using the chef
command, to manage a default Virtual Host:
$ chef generate recipe cookbooks/apache virtualhost
A new file named apache/recipes/virtualhost.rb
is now created.
To store the name of our virtual host, let's create an attribute
. An attribute is similar to a persisting node setting, declared in a cookbook in a file under the attribute
directory, and can then be overridden by many mechanisms that we'll later discover. Start by generating an attributes file using the chef
command:
$ chef generate attribute cookbooks/apache default
This will create a new file under apache/attributes/default.rb
. To set the sitename
attribute with default value of defaultsite
, add the following in this file:
default["sitename"] = "defaultsite"
To create a new directory, let's use a resource named directory
in the apache/recipes/virtualhost.rb
file, with standard access rights. Note the Ruby #{node["sitename"]}
syntax to access a node attribute from inside a string that will be recurring from now on:
directory "/var/www/#{node["sitename"]}" do owner 'root' group 'root' mode '0755' action :create end
Let's reuse the file
resource to create a basic index.html
file with a simple string such as Hello from Chef!
or whatever you find more appealing, in the apache/recipes/virtualhost.rb
file:
file "/var/www/#{node["sitename"]}/index.html" do owner 'root' group 'root' mode '0644' content '<html><h1>Hello from Chef!</h1></html>' end
Let's once again use the chef
generator to create a new template for our Apache virtual host configuration file:
$ chef generate template cookbooks/apache virtualhost
This will create a template under apache/templates/
named virtuahost.erb
. This is a standard ERB (short for Embedded Ruby) template. This template file will contain the virtual host Apache configuration for our site.
Let's start by populating the content of this ERB with a minimal Apache configuration file, using a new website
variable that we'll set in a minute.
<VirtualHost *:80> ServerName <%= @website %> DocumentRoot /var/www/<%= @website %> ErrorLog /var/log/httpd/error-<%= @website %>.log CustomLog /var/log/httpd/access-<%= @website %>.log combined <Directory /var/www/<%= @website %>/ > Options Indexes FollowSymLinks MultiViews AllowOverride All Order allow,deny allow from all </Directory> </VirtualHost>
This way, the whole configuration is dynamic; we'll be able to instantiate this cookbook for any site name of our choice and it will be dedicated to it.
Now let's use the template
resource to generate a file from the template we just created, in the apache/recipes/virtualhost.rb
file. This resource takes a source
parameter, which is the template file we just created, and the variables to be injected. In our case, we want to inject the value of the sitename
attribute, so it can be accessed by the template as @website
:
template "/etc/httpd/conf.d/#{node["sitename"]}.conf" do source "virtualhost.erb" owner 'root' group 'root' mode '0644' variables( :website => "#{node["sitename"]}" ) end
Don't forget to bump the cookbook version in apache/metadata.rb
:
version '0.3.0'
Upload the cookbook to Chef server from the workstation:
$ knife cookbook upload apache
Add the newly-created recipe to the remote node run list:
$ knife node run_list add <node name> apache::virtualhost
Apply the new cookbook on the remote host:
$ sudo chef-client
Restart the Apache server manually to take the changes into account (be sure we'll automate that in the next pages):
$ sudo systemctl restart httpd
Verify that the served page is the one we added:
$ curl http://node_ip_or_hostname/ <html><h1>Hello from Chef!</h1></html>
Good job! We've just covered how to manage files, directories, as well as dynamic templates, using pure Ruby code with Chef.
Now that we have a LAMP server with Puppet, let's create a virtual host! Our goals are as follows:
DocumentRoot
and dedicated log filesphpinfo()
These three operations will be done using the file
directive.
On Ubuntu, we need to remove the default website in order to have virtual hosting up and running. This can be done easily in the Apache manifest; a file
directive for the deletion of /etc/apache2/site-enabled/000-default.conf
will remove the symlink and will disable the site:
class apache { package {'apache2': ensure => present, } service {'apache2': ensure => running, enable => true } file {'/etc/apache2/sites-enabled/000-default.conf': ensure => absent, } }
Now let's create the code for the virtual host generation. The creation of a new virtual host must be done in /etc/apache2/sites-available
, and will be generated from a template. Two languages are available:
Our EPP template will use two parameters: the site name and the document root. Let's create a vhost.epp
file in the modules/apache/templates
directory:
<VirtualHost *:80> ServerName <%=$website%> DocumentRoot <%=$docroot%> <Directory <%=$docroot%>> Order deny,allow Allow from all AllowOverride All </Directory> ErrorLog /var/log/apache2/error-<%=$website%>.log CustomLog /var/log/apache2/access-<%=$website%>.log combined </VirtualHost>
Now we need to instantiate this template. The best way is to think about something we could reuse as many times as needed (in case we would like to add more sites).
We previously used a class statement, but each class
in Puppet can be used only once per catalog (remember, a catalog is the result of the compilation for a node). Fortunately, the define
statement is used to define a block of code that can be used multiple times.
So let's define a file, module/apache/manifest/vhost.pp
that will use such a statement:
define apache::vhost ( $website, $docroot ) { file { "/etc/apache2/sites-available/$website.conf": ensure => present, owner => 'root', group => 'root', mode => '0640', content => epp('apache/vhost.epp', {'website' => $website, 'docroot'=>$docroot}), } file { "/etc/apache2/sites-enabled/$website.conf": ensure => link, target => "/etc/apache2/sites-available/$website.conf", require => File["/etc/apache2/sites-available/$website.conf"], } }
The website name and the document root are the two parameters for our apache::vhost
statement and are passed to the epp
function along with the template file name in the first file
directive.
On Ubuntu, to enable a site, a link must be created in /etc/apache2/site-enabled
; the second file
directive will handle it.
Finally, we need to deploy our PHP file under the DocumentRoot
directory. This can be done directly in the main manifest using file
directives to create the DocumentRoot
directory and the file itself:
node 'web.pomes.pro' { $website=$fqdn; $docroot="/var/www/$fqdn"; class { 'apache':; 'php':; 'mariadb':; } apache::vhost {$website: website => $website, docroot => $docroot, } file { $docroot: ensure => directory, owner => 'www-data', group => 'www-data', mode => '0755', } file {"$docroot/index.php": ensure => present, owner => 'www-data', group => 'www-data', mode => '0644', content => "<?php phpinfo() ?>", } }
We can now run the Puppet agent again. For now, we need to restart Apache manually in order to have our virtual host running (as for Chef, we'll automate this in the next pages):
root@web:~# service apache2 reload
Now you should see the phpinfo page on http://web.pomes.pro
file
resource documentation: https://docs.puppet.com/puppet/4.8/types/file.html18.226.187.24