Zero Downtime Deployment with Ansible
What's a provisioning framework?
Automated setup of servers
Configuration as code
Examples
Create users
Install software
Generate and manipulate config files
Start/stop/restart processes
Set up dependencies between operations
Describe what to do
#!/bin/bash
if $( command -v vim >/dev/null 2>&1 ); then
echo "vim is already installed."
else
apt-get install vim
fi
if $( grep -Fxq "filetype indent off" /etc/vim/vimrc ); then
echo "set filetype indent off is already in /etc/vim/vimrc."
else
echo "filetype indent off" >> /etc/vim/vimrc
# TODO: Do not continue if this fails.
fi
# TODO: Rollback if something fails.
Describe state
- name: ensure installed vim
apt: pkg=vim state=installed
- name: set filetype indent off for vim
lineinfile:
dest=/etc/vim/vimrc
line='filetype indent off'
state=present
Ansible
SSH-based
Client only (no server)
YAML configuration
Push (and pull)
Supports more than setup and provisioning:
Application deployment
Remote command execution
Bring up the boxes
vagrant up
Layout
├── ansible.cfg
├── hosts
├── site.yml
├── group_vars
│ └── <group name>
├── host_vars
│ └── <host name>
├── roles
│ ├── <role>
│ │ ├── files
│ │ └── <file>
│ │ └── templates
│ │ └── <template>.j2
│ │ ├── handlers
│ │ │ └── main.yml
│ │ ├── tasks
│ │ │ └── main.yml
Explain all folders and files.
Play!
ansible-playbook site.yml
Facts
Ansible by default gathers “facts” about the machines under management.
These facts can be accessed in Playbooks and in templates.
ansible -m setup app1.local
The task
An app user 'devops', with:
Home directory: /home/devops
ssh-key
A PostgresSQL database.
Nginx as a reverse proxy.
An init script installed as a service.
Deploy an application that uses the provisioned infrastructure.
Task1: Solution
git diff HEAD origin/task1
git checkout task1 # or keep your own solution
ansible-playbook site.yml --tags apt,vim
ProTip: Use '--tags', '--skip-tags', '--limit' and/or 'gather_facts: False' to reduce execution time.
Progress
Installed software
Manipulated files
Task2: Solution
git diff HEAD origin/task2
git checkout task2 # or keep your own solution
ansible-playbook site.yml --limit appservers --skip-tags apt,vim,java
ssh devops@app1.local
Progress
Installed software
Manipulated files
Created a user and set up a ssh-key
Task3: Solution
git diff HEAD origin/task3
git checkout task3 # or keep your own solution
ansible-playbook site.yml --limit dbservers --tags pg_install
$ vagrant ssh db
vagrant@db:~$ psql -d devops -U devops -W
devops=> \q
Progress
Installed software
Manipulated files
Created a user and set up a ssh-key
Installed and configured a database and a db user
Task4: Deploy!
roles/app
├── files
│ └── init.sh
├── tasks
│ └── main.yml
└── templates
└── config.properties.j2
NB! Use variables (./hosts).
Set 'serial: 1' for appservers in site.yml.
Help:
http://docs.ansible.com/service_module.html
Task4: Solution
Browse to http://app1.local:1234/
git diff HEAD origin/task4
git checkout task4 # or keep your own solution
ansible-playbook site.yml --limit appservers --tags deploy
What just happened?
/home/devops
├── config.properties
├── current -> /home/devops/devops_1416228023.jar
├── previous -> /home/devops/devops_1416221573.jar
├── devops_1416221573.jar
├── devops_1416228023.jar
└── logs
├── stderr.log
└── stdout.log
/etc/init.d
└── devops
Progress
Installed software
Manipulated files
Created a user and set up a ssh-key
Installed and configured a database and a db user
Deployed an application to two appservers and enabled it as a service
Task5: Solution
Browse to http://app1.local:1234/
git diff HEAD origin/task5
git checkout task5 # or keep your own solution
ansible-playbook site.yml --limit dbservers --tags deploy
$ vagrant ssh db
vagrant@db:~$ psql -d devops -U devops -W
devops=> \dt
devops=> select * from hello;
devops=> \q
Progress
Installed software
Manipulated files
Created a user and set up a ssh-key
Installed and configured a database and a db user
Deployed an application to two appservers and enabled it as a service
Migrated the database schema and fetched data from it through the application
Task6: Solution
Browse to http://proxy.local/ # refresh me many times
git diff HEAD origin/task6
git checkout task6 # or keep your own solution
ansible-playbook site.yml --limit proxies --tags nginx
Progress
Installed software
Manipulated files
Created a user and set up a ssh-key
Installed and configured a database and a db user
Deployed an application to two appservers and enabled it as a service
Migrated the database schema and fetched data from it through the application
Set up a reverse proxy for automatic failover between the two appservers
The Expand/Contract pattern
Expand
Contract
Add tables
Add columns
Tweak indexes
Remove tables
Remove columns
Remove/add constraints
Play time :-)
Suggestions:
Change database table name from HELLO to MESSAGES and deploy a new version without downtime.
Implement automated rollback.
I have been playing :-)
git checkout play
ansible-playbook site.yml --limit appservers,dbservers --tags deploy
ansible-playbook site.yml --limit appservers,dbservers --tags rollback