Ansible is a powerful automation engine that simplifies deploying systems and apps. Its popularity has been rising rapidly as developers and system administrators look for simpler ways to manage servers and deploy applications.
The selling points of Ansible are:
- simplicity: the configuration is done through INI and YAML files
- agentless: there is no agent to install, making it dead easy to use on virtual servers and shared hosting
- extensible: thanks to roles and modules, it is very easy to extend its functionality and reuse of configuration blocks
If you follow our blog you will know that here at ServerGrove we have been promoting the use of Capifony, an extension for Capistrano that is targeted to the deployment of Symfony2 applications. However Capifony & Capistrano are based on Ruby, and every time we run into an issue, we need to dig to find out where the problem lies. In addition, with every major upgrade of Capistrano, something breaks, especially in our case where we have a fairly complex deployment process. So, we decided to give Ansible a try.
Installing and setting up Ansible
Installing Ansible is simple. The only requirement is to have Python installed in the computer where you will be launching the Ansible process. You can follow the manual instructions which recommends several ways to get Ansible running.
Once it is installed, Ansible will connect over SSH to the remote servers that it controls. The servers that you want to control need to be listed in a simple text file following the INI format, this is called the inventory:
192.168.1.50 aserver.example.org bserver.example.org
You can group servers in sets so you can define a list of servers for testing, qa, production, etc.
[testing] test1.example.com [prod] www1.example.com www2.example.com www[10:50].example.com
There are more complex groupings you can do, so it covers pretty much any need you can think of.
Now, you can execute simple commands with Ansible:
# send a ping command to all servers $ ansible all -m ping # print hostname and logged in users $ ansible all -a "hostname -f && w"
You can also copy files:
$ ansible prod -m copy -a "src=/etc/hosts dest=/tmp/hosts"
You can manage users, OS packages (rpm, deb, etc), services (ie. stop/start/restart apache) and much more thanks to modules.
The real power comes when you start defining these commands as tasks:
--- - hosts: webservers vars: http_port: 80 max_clients: 200 remote_user: root tasks: - name: ensure apache is at the latest version yum: pkg=httpd state=latest - name: write the apache config file template: src=/srv/httpd.j2 dest=/etc/httpd.conf notify: - restart apache - name: ensure apache is running service: name=httpd state=started handlers: - name: restart apache service: name=httpd state=restarted
The above code tells Ansible that these tasks apply to all hosts in the group “webservers”, then we define the variables, and then create a list of tasks that will be run in sequence. If any of these tasks fail, the process will stop.
All of these can be store in files called playbooks, and these playbooks can depend on other playbooks, including external files, etc, making it really extensible and easy.
Furthermore, you can combine these playbooks into “roles” (similar to Symfony2’s bundles), so you can reuse these across projects. These roles can be shared by uploading them to Ansible Galaxy, a site where people share roles for common needs.
There is a lot more that Ansible can do, but for this article we want to focus on Symfony2, for more information we recommend that you read the excellent Ansible documentation.
As you may know, successfully deploying a Symfony2 application involves several steps, the most common ones are:
- define a directory structure in the server which will allow to setup the Symfony2 application before making it live and keep multiple releases, so you can rollback to a previous release if something fails.
- clone your git repository
- run composer install to get all your dependencies
- warm up the cache / dump Assetic assets
- update the web server document root to point the site to the new release
If you used Capifony before, these are more or less the steps that are done every time you run cap deploy.
In Ansible this is easily achievable by defining a set of tasks in a playbook and then running them and you can put all of these in a role, so you can reuse it in all your Symfony2 projects.
Introducing the Ansible Symfony2 Deployment Role
To use the role in your project, once you installed Ansible, run the following command to make the role available in your computer:
$ ansible-galaxy install servergrove.symfony2
The next step is to define the inventory (the list of servers where you will deploy your application). Create a file app/config/hosts.ini
Next, you need to create a YAML file that will contain the variables and basic instructions for Ansible, name it app/config/deploy.yml
--- - hosts: prod vars: symfony2_project_name: your_project symfony2_project_root: /var/www/vhosts/example.com symfony2_project_repo: email@example.com:username/your_repo.git ansible_ssh_user: your_ssh_username roles: - servergrove.symfony2 tasks: - local_action: osx_say msg="Deployment complete." voice=Zarvox
We defined a configuration for the prod group of servers. We define the variables needed for the role to deploy our project. It will run the role and when it finishes it will run the osx_say task locally that will inform us that the process has completed (this only works in Mac OSX, but you can replace it with an action that will send an email or some other form of notification).
Once you have these, you are ready to deploy your application with Ansible, do it with the following command:
$ ansible-playbook -l prod -i app/config/hosts.ini -e "symfony2_project_release=1" app/config/deploy.yml -vvv
- the -l option defines which list of servers we want to run this for
- the -i option defines the inventory file that contains the list of servers
- the -e option allows us to define variables that will be used in the playbook. In this case we tell Ansible that we will deploy a release named “1”. You can replace this with `date +%Y%m%d%H%M%S` to name our releases with the current date/time.
- Then we define the playbook we will use and the -vvv option allows us to see what Ansible is doing.
The servergrove.symfony2 role will perform the following tasks:
- creates the directory structure to host your app, following a similar structure from Capifony:
- clones the git repository
- if composer is not installed or is outdated, it will download the phar in the project directory
- runs composer install –no-dev –optimize-autoload
- runs app/console assetic:dump
- creates/updates current symlink to point to new release
This is the first version of this role, we hope to improve the functionality with new features, if you have any suggestions, please open an issue or send your pull request.
As we said before, Ansible does not need anything special in the server, it only needs to be able to connect to the server using SSH. This means that it can be used with any of our VPS plans and in all Developer and Business shared hosting plans, making the deployment of Symfony2 apps really easy and predictable.