Setting up Ansible for hourly Rails log rotation
Logs are an important part of any web application. Too few logs make debugging issues a problem. On the flip-side, too many logs can make a disk max out of space, or finding a specific error harder than it should be.
I was recently tasked with setting up hourly log rotation for a server that was running Rails, deployed via Capistrano.
Here is the Ansible setup which I used to programmatically add the rotate logic to cron. Please note that this setup is for an Ubuntu server, and requires that you input the sudo password when running the Ansible playbook. It also sets up the cron job to run under the root user's cron file. It would be possible to have it run on another user's cron file but that option could wipe out the crontab.
The directory structure
The file content
000_cross_group_env_vars.ymlThe files ./production/group_vars/all/000_cross_group_env_vars.yml and ./staging/group_vars/all/000_cross_group_env_vars.yml should be symlinks to the file 000_cross_group_env_vars.yml which is at the base of the directory tree. The latter should contain something like the following:
---
base_os:
packages:
- vim
- cron
- logrotate
logrotate:
rails_file: /etc/logrotate.d/rails
You can then create the symlinks with the following commands:
cd environments/staging/group_vars/all
ln -s ../../../../000_cross_group_env_vars.yml ./000_cross_group_env_vars.yml
cd environments/production/group_vars/all
ln -s ../../../../000_cross_group_env_vars.yml ./000_cross_group_env_vars.yml
./bin/provision
This is a simple bash file which should be executable (i.e. you should run chmod +x provision)
#!/bin/bash
ansible-playbook -i ./environments/$@ playbook.yml --ask-become-pass
hosts
These files should contain a list of the staging or production servers, for example:
[all]
123.50.180.123
./roles/base_os/tasks/main.yml
This should contain the rules to install the base packages which are necessary on your server:
---
- name: Ensure required packages installed
apt:
update_cache: yes
name: "{{ item }}"
state: latest
with_items: "{{ base_os.packages }}"
./roles/logrotate/tasks/main.yml
This should copy over the logrotate file and create the cron entry:
---
- name: Add Rails app logrotate rules
template:
src: templates/logrotate_rails.j2
dest: "{{ logrotate.rails_file }}"
owner: root
group: root
mode: '0644'
force: yes
- name: Set up cron
cron:
name: Hourly log rotation
minute: "0"
hour: "*"
user: root
job: "/usr/sbin/logrotate -f {{ logrotate.rails_file }} > /dev/null"
./roles/logrotate/templates/logrotate_rails.j2
This file contains the template with the logrotate directives:
# {{ ansible_managed }}
# Path to your Rails app:
"/var/www/app/current/log/*.log" {
hourly
su deploy deploy
size 25M
maxsize 50M
rotate 150
missingok
notifempty
compress
delaycompress
copytruncate
}
playbook.yml
This is the main entry-point that the Ansible playbook will run:
---
- hosts: all
become: true
remote_user: deploy # The remote user that the ansible script will run as
roles:
- base_os
- logrotate
Conclusion
With this setup in place you will be able to easily add log rotation to a Rails application via Ansible, simply by running:
./bin/provision staging
# or
./bin/provision production