├── LICENSE ├── README.md ├── Vagrantfile ├── hosts ├── playbook.yml ├── profiling.yml └── roles ├── database ├── files │ ├── db.php │ └── dump.sql └── tasks │ ├── .main.yml.swp │ └── main.yml ├── profiling ├── files │ ├── apache2 │ │ └── vhosts │ │ │ └── xhgui.conf │ ├── mysql │ │ └── details.sql │ ├── php │ │ └── mods-available │ │ │ ├── xdebug.ini │ │ │ └── xhprof.ini │ ├── profiling.php │ └── xhgui │ │ └── config.php ├── handlers │ └── main.yml └── tasks │ ├── install_demo.yml │ ├── install_prerequisites.yml │ ├── install_xdebug.yml │ ├── install_xhgui.yml │ ├── install_xhprof.yml │ └── main.yml └── webserver ├── files └── index.php └── tasks └── main.yml /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2013 qandidate-labs 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ansible-lamp-server 2 | =================== 3 | 4 | example code used in: 5 | 6 | [Installing a LAMP server with Ansible playbooks and roles](http://labs.qandidate.com/blog/2013/11/21/installing-a-lamp-server-with-ansible-playbooks-and-roles/) 7 | 8 | [Setting up XHProf/XHGui profiling with Ansible](http://labs.qandidate.com/blog/2013/11/28/setting_up_xhprof_xhgui_profiling_with_ansible/) 9 | -------------------------------------------------------------------------------- /Vagrantfile: -------------------------------------------------------------------------------- 1 | # -*- mode: ruby -*- 2 | # vi: set ft=ruby : 3 | 4 | # Vagrantfile API/syntax version. Don't touch unless you know what you're doing! 5 | VAGRANTFILE_API_VERSION = "2" 6 | 7 | Vagrant.configure(VAGRANTFILE_API_VERSION) do |config| 8 | # All Vagrant configuration is done here. The most common configuration 9 | # options are documented and commented below. For a complete reference, 10 | # please see the online documentation at vagrantup.com. 11 | 12 | # Every Vagrant virtual environment requires a box to build off of. 13 | config.vm.box = "ansible" 14 | 15 | # The url from where the 'config.vm.box' box will be fetched if it 16 | # doesn't already exist on the user's system. 17 | config.vm.box_url = "http://cloud-images.ubuntu.com/vagrant/raring/current/raring-server-cloudimg-amd64-vagrant-disk1.box" 18 | 19 | # Create a forwarded port mapping which allows access to a specific port 20 | # within the machine from a port on the host machine. In the example below, 21 | # accessing "localhost:8080" will access port 80 on the guest machine. 22 | # config.vm.network :forwarded_port, guest: 80, host: 8080 23 | 24 | # Create a private network, which allows host-only access to the machine 25 | # using a specific IP. 26 | config.vm.network :private_network, ip: "10.0.0.10" 27 | 28 | # Create a public network, which generally matched to bridged network. 29 | # Bridged networks make the machine appear as another physical device on 30 | # your network. 31 | # config.vm.network :public_network 32 | 33 | # If true, then any SSH connections made will enable agent forwarding. 34 | # Default value: false 35 | # config.ssh.forward_agent = true 36 | 37 | # Share an additional folder to the guest VM. The first argument is 38 | # the path on the host to the actual folder. The second argument is 39 | # the path on the guest to mount the folder. And the optional third 40 | # argument is a set of non-required options. 41 | # config.vm.synced_folder "../data", "/vagrant_data" 42 | 43 | # Provider-specific configuration so you can fine-tune various 44 | # backing providers for Vagrant. These expose provider-specific options. 45 | # Example for VirtualBox: 46 | # 47 | # config.vm.provider :virtualbox do |vb| 48 | # # Don't boot with headless mode 49 | # vb.gui = true 50 | # 51 | # # Use VBoxManage to customize the VM. For example to change memory: 52 | # vb.customize ["modifyvm", :id, "--memory", "1024"] 53 | # end 54 | # 55 | # View the documentation for the provider you're using for more 56 | # information on available options. 57 | 58 | # Enable provisioning with Puppet stand alone. Puppet manifests 59 | # are contained in a directory path relative to this Vagrantfile. 60 | # You will need to create the manifests directory and a manifest in 61 | # the file ansible.pp in the manifests_path directory. 62 | # 63 | # An example Puppet manifest to provision the message of the day: 64 | # 65 | # # group { "puppet": 66 | # # ensure => "present", 67 | # # } 68 | # # 69 | # # File { owner => 0, group => 0, mode => 0644 } 70 | # # 71 | # # file { '/etc/motd': 72 | # # content => "Welcome to your Vagrant-built virtual machine! 73 | # # Managed by Puppet.\n" 74 | # # } 75 | # 76 | # config.vm.provision :puppet do |puppet| 77 | # puppet.manifests_path = "manifests" 78 | # puppet.manifest_file = "site.pp" 79 | # end 80 | 81 | # Enable provisioning with chef solo, specifying a cookbooks path, roles 82 | # path, and data_bags path (all relative to this Vagrantfile), and adding 83 | # some recipes and/or roles. 84 | # 85 | # config.vm.provision :chef_solo do |chef| 86 | # chef.cookbooks_path = "../my-recipes/cookbooks" 87 | # chef.roles_path = "../my-recipes/roles" 88 | # chef.data_bags_path = "../my-recipes/data_bags" 89 | # chef.add_recipe "mysql" 90 | # chef.add_role "web" 91 | # 92 | # # You may also specify custom JSON attributes: 93 | # chef.json = { :mysql_password => "foo" } 94 | # end 95 | 96 | # Enable provisioning with chef server, specifying the chef server URL, 97 | # and the path to the validation key (relative to this Vagrantfile). 98 | # 99 | # The Opscode Platform uses HTTPS. Substitute your organization for 100 | # ORGNAME in the URL and validation key. 101 | # 102 | # If you have your own Chef Server, use the appropriate URL, which may be 103 | # HTTP instead of HTTPS depending on your configuration. Also change the 104 | # validation key to validation.pem. 105 | # 106 | # config.vm.provision :chef_client do |chef| 107 | # chef.chef_server_url = "https://api.opscode.com/organizations/ORGNAME" 108 | # chef.validation_key_path = "ORGNAME-validator.pem" 109 | # end 110 | # 111 | # If you're using the Opscode platform, your validator client is 112 | # ORGNAME-validator, replacing ORGNAME with your organization name. 113 | # 114 | # If you have your own Chef Server, the default validation client name is 115 | # chef-validator, unless you changed the configuration. 116 | # 117 | # chef.validation_client_name = "ORGNAME-validator" 118 | end 119 | -------------------------------------------------------------------------------- /hosts: -------------------------------------------------------------------------------- 1 | #hosts 2 | 10.0.0.10 ansible_ssh_user=vagrant ansible_ssh_private_key_file=~/.vagrant.d/insecure_private_key 3 | 4 | -------------------------------------------------------------------------------- /playbook.yml: -------------------------------------------------------------------------------- 1 | # playbook.yml 2 | --- 3 | - hosts: all 4 | roles: 5 | - webserver 6 | - database 7 | -------------------------------------------------------------------------------- /profiling.yml: -------------------------------------------------------------------------------- 1 | # profiling.yml 2 | --- 3 | - hosts: all 4 | roles: 5 | - webserver 6 | - database 7 | - profiling 8 | -------------------------------------------------------------------------------- /roles/database/files/db.php: -------------------------------------------------------------------------------- 1 | # roles/database/files/db.php 2 | query('SELECT message FROM demo'); 6 | 7 | echo $statement->fetchColumn(); 8 | 9 | -------------------------------------------------------------------------------- /roles/database/files/dump.sql: -------------------------------------------------------------------------------- 1 | # roles/database/files/dump.sql 2 | CREATE TABLE IF NOT EXISTS demo ( 3 | message varchar(255) NOT NULL 4 | ) ENGINE=MyISAM DEFAULT CHARSET=utf8; 5 | 6 | INSERT INTO demo (message) VALUES('Hello World!'); 7 | -------------------------------------------------------------------------------- /roles/database/tasks/.main.yml.swp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qandidate-labs/ansible-lamp-server/04e4899dfc416ed7804c42289f2c1c7633b2db5c/roles/database/tasks/.main.yml.swp -------------------------------------------------------------------------------- /roles/database/tasks/main.yml: -------------------------------------------------------------------------------- 1 | # roles/database/tasks/main.yml 2 | --- 3 | - name: 1a. Add APT GPG signing key 4 | apt_key: url=http://keyserver.ubuntu.com/pks/lookup?op=get&search=0xCBCB082A1BB943DB state=present 5 | 6 | - name: 1b. Add APT repository 7 | apt_repository: repo='deb http://ftp.osuosl.org/pub/mariadb/repo/10.0/ubuntu $ansible_distribution_release main' state=present update_cache=yes 8 | 9 | - name: 1c. Install MariaDB server package 10 | apt: name=mariadb-server state=present 11 | 12 | - name: 2. Start Mysql Service 13 | service: name=mysql state=started enabled=true 14 | 15 | - name: Install python Mysql package #required for mysql_db tasks 16 | apt: name=python-mysqldb state=present 17 | 18 | - name: 3. Create a new database 19 | mysql_db: name=demo state=present collation=utf8_general_ci 20 | 21 | - name: 4. Create a database user 22 | mysql_user: name=demo password=demo priv=*.*:ALL host=localhost state=present 23 | 24 | - name: 5a. Copy sample data 25 | copy: src=dump.sql dest=/tmp/dump.sql 26 | 27 | - name: 5b. Insert sample data 28 | shell: cat /tmp/dump.sql | mysql -u demo -pdemo demo 29 | 30 | - name: 6. Install MySQL extension for PHP 31 | apt: name=php5-mysql state=present 32 | 33 | -------------------------------------------------------------------------------- /roles/profiling/files/apache2/vhosts/xhgui.conf: -------------------------------------------------------------------------------- 1 | 2 | DocumentRoot "/data/xhgui/xhprof_html" 3 | ServerName xhgui.local 4 | ErrorLog "/var/log/apache2/xhgui.localhost-error.log" 5 | CustomLog "/var/log/apache2/xhgui.localhost-access.log" combined 6 | 7 | 8 | AllowOverride All 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /roles/profiling/files/mysql/details.sql: -------------------------------------------------------------------------------- 1 | # taken from xhgui/xhprof_lib/utils/Db/Mysqli.php 2 | CREATE TABLE IF NOT EXISTS `details` ( 3 | `id` char(17) NOT NULL, 4 | `url` varchar(255) default NULL, 5 | `c_url` varchar(255) default NULL, 6 | `timestamp` timestamp NOT NULL default CURRENT_TIMESTAMP on update CURRENT_TIMESTAMP, 7 | `server name` varchar(64) default NULL, 8 | `perfdata` MEDIUMBLOB, 9 | `type` tinyint(4) default NULL, 10 | `cookie` BLOB, 11 | `post` BLOB, 12 | `get` BLOB, 13 | `pmu` int(11) unsigned default NULL, 14 | `wt` int(11) unsigned default NULL, 15 | `cpu` int(11) unsigned default NULL, 16 | `server_id` char(3) NOT NULL default 't11', 17 | `aggregateCalls_include` varchar(255) DEFAULT NULL, 18 | PRIMARY KEY (`id`), 19 | KEY `url` (`url`), 20 | KEY `c_url` (`c_url`), 21 | KEY `cpu` (`cpu`), 22 | KEY `wt` (`wt`), 23 | KEY `pmu` (`pmu`), 24 | KEY `timestamp` (`timestamp`) 25 | ) ENGINE=MyISAM DEFAULT CHARSET=utf8; 26 | -------------------------------------------------------------------------------- /roles/profiling/files/php/mods-available/xdebug.ini: -------------------------------------------------------------------------------- 1 | zend_extension=/usr/lib/php5/20100525/xdebug.so 2 | xdebug.remote_enable=1 3 | xdebug.remote_port=9000 4 | ;xdebug.profiler_enable=1 5 | ;xdebug.profiler_output_dir="/tmp/xdebug" -------------------------------------------------------------------------------- /roles/profiling/files/php/mods-available/xhprof.ini: -------------------------------------------------------------------------------- 1 | extension=xhprof.so -------------------------------------------------------------------------------- /roles/profiling/files/profiling.php: -------------------------------------------------------------------------------- 1 | \n"; 6 | 7 | require_once '/data/xhgui/external/footer.php'; 8 | -------------------------------------------------------------------------------- /roles/profiling/files/xhgui/config.php: -------------------------------------------------------------------------------- 1 | 'load::', 101 | 'mysql' => 'mysql_' 102 | ); 103 | 104 | // For domain-specific configuration, you can use Apache setEnv xhprof_aggregateCalls_include [some_php_file] 105 | if(isset($run_details['aggregateCalls_include']) && strlen($run_details['aggregateCalls_include']) > 1) 106 | { 107 | require_once($run_details['aggregateCalls_include']); 108 | } 109 | 110 | $addIns = array(); 111 | foreach($calls as $index => $call) 112 | { 113 | foreach($rules as $rule => $search) 114 | { 115 | if (strpos($call['fn'], $search) !== false) 116 | { 117 | if (isset($addIns[$search])) 118 | { 119 | unset($call['fn']); 120 | foreach($call as $k => $v) 121 | { 122 | $addIns[$search][$k] += $v; 123 | } 124 | }else 125 | { 126 | $call['fn'] = $rule; 127 | $addIns[$search] = $call; 128 | } 129 | unset($calls[$index]); //Remove it from the listing 130 | break; //We don't need to run any more rules on this 131 | }else 132 | { 133 | //echo "nomatch for $search in {$call['fn']}
\n"; 134 | } 135 | } 136 | } 137 | return array_merge($addIns, $calls); 138 | } 139 | -------------------------------------------------------------------------------- /roles/profiling/handlers/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: restart apache 3 | service: name=apache2 state=restarted -------------------------------------------------------------------------------- /roles/profiling/tasks/install_demo.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Install demo PHP script 3 | copy: src=profiling.php dest=/var/www/ mode=0644 4 | tags: profiling 5 | -------------------------------------------------------------------------------- /roles/profiling/tasks/install_prerequisites.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Install PECL 3 | apt: pkg=php-pear state=present 4 | tags: profiling 5 | 6 | - name: Install php5-dev for phpize 7 | apt: pkg=php5-dev state=present 8 | tags: profiling 9 | 10 | - name: Install git 11 | apt: pkg=git state=present 12 | tags: profiling 13 | 14 | - name: install Graphviz 15 | apt: name=graphviz state=present 16 | tags: profiling 17 | -------------------------------------------------------------------------------- /roles/profiling/tasks/install_xdebug.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Install xdebug PECL extension 3 | shell: pecl install --soft xdebug 4 | ignore_errors: true 5 | tags: profiling 6 | 7 | - name: Create extension ini file in mods-available 8 | copy: src=php/mods-available/xdebug.ini dest=/etc/php5/mods-available/ owner=root mode=644 9 | tags: profiling 10 | 11 | - name: Enable module 12 | shell: php5enmod xdebug 13 | notify: restart apache 14 | tags: profiling 15 | -------------------------------------------------------------------------------- /roles/profiling/tasks/install_xhgui.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: clone XHGui 3 | git: repo=https://github.com/preinheimer/xhprof.git dest=/data/xhgui 4 | tags: profiling 5 | 6 | - name: copy config.php 7 | copy: src=xhgui/config.php dest=/data/xhgui/xhprof_lib/ mode=0644 8 | tags: profiling 9 | 10 | - name: Create XHGUI Database 11 | mysql_db: name=xhgui state=present collation=utf8_general_ci 12 | tags: profiling 13 | 14 | - name: Create XHGUI DB User 15 | mysql_user: name=xhgui password=xhgui priv=xhgui.*:ALL host=localhost state=present 16 | tags: profiling 17 | 18 | - name: Provision XHGUI DB (copy dump file) 19 | copy: src=mysql/details.sql dest=/tmp/development.sql 20 | tags: profiling 21 | 22 | - name: Provision XHGUI DB (import dump file) 23 | shell: cat /tmp/development.sql | mysql -u xhgui -pxhgui xhgui 24 | tags: profiling 25 | 26 | - name: copy Apache vhost config 27 | copy: src=apache2/vhosts/xhgui.conf dest=/etc/apache2/sites-available/xhgui.conf 28 | tags: apache 29 | 30 | - name: enable vhost 31 | shell: a2ensite xhgui.conf 32 | notify: restart apache 33 | tags: profiling 34 | -------------------------------------------------------------------------------- /roles/profiling/tasks/install_xhprof.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Install XHProf PECL extension 3 | shell: pecl install --soft xhprof-beta 4 | ignore_errors: true 5 | tags: profiling 6 | 7 | - name: Create extension ini file in mods-available # echo "extension=xhprof.so > xhprof.ini" 8 | copy: src=php/mods-available/xhprof.ini dest=/etc/php5/mods-available/ owner=root mode=0644 9 | tags: profiling 10 | 11 | - name: Enable module 12 | shell: php5enmod xhprof 13 | notify: restart apache 14 | tags: profiling 15 | -------------------------------------------------------------------------------- /roles/profiling/tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - include: install_prerequisites.yml 3 | - include: install_xhprof.yml 4 | - include: install_xhgui.yml 5 | - include: install_xdebug.yml 6 | - include: install_demo.yml 7 | -------------------------------------------------------------------------------- /roles/webserver/files/index.php: -------------------------------------------------------------------------------- 1 |