An Ansible role for implementing zero-downtime rolling deployments across a cluster of nodes. This role enables controlled, sequential node updates by temporarily blocking upstream load balancer connections using iptables, ensuring high availability during deployments.
- 🔄 Zero-downtime deployments - Maintain service availability during updates
- 🎯 Blue/green deployment support - Seamless traffic switching between node sets
- 🔒 Iptables-based traffic control - Block/unblock load balancer connections
- 🐳 Docker and non-Docker compatible - Works with both deployment types
- 🌐 IPv4 and IPv6 support - Dual-stack network compatibility
- ⚡ Sequential deployment control - Configurable serial execution
- Ansible: 2.9 or higher
- OS: Linux distribution with iptables support (Ubuntu, Debian, CentOS/RHEL)
- Privileges: Root/sudo access for iptables management
- Network: Upstream load balancers configured to respect connection blocks
Install from Ansible Galaxy:
ansible-galaxy install fccn.ansible_rolling_deployOr add to requirements.yml:
roles:
- name: fccn.ansible_rolling_deploy
version: main| Variable | Description | Example |
|---|---|---|
rolling_deploy_parent_servers_ipv4 |
List of IPv4 addresses or hostnames of load balancers | ['172.24.1.81', '172.24.1.82'] |
| Variable | Default | Description |
|---|---|---|
rolling_deploy_starting |
false |
Set to true to block connections, false to unblock |
rolling_deploy_parent_servers_ipv6 |
[] |
List of IPv6 addresses of load balancers |
rolling_deploy_iptables_state |
Auto-calculated | Iptables rule state (present or absent) |
rolling_deploy_chains |
[FORWARD, INPUT] |
Iptables chains to modify |
See defaults/main.yml for complete variable definitions.
rolling_deploy_parent_servers_ipv4:
- 172.24.1.81
- 172.24.1.82
- myloadbalancer.priv.fccn.ptDefine load balancers in your inventory:
[load_balancers]
lb01-dev.myservice.fccn.pt ansible_host=172.24.1.81
lb02-dev.myservice.fccn.pt ansible_host=172.24.1.82Reference in playbook:
rolling_deploy_parent_servers_ipv4: "{{ groups['load_balancers'] | map('extract', hostvars, ['ansible_host']) | list }}"rolling_deploy_parent_servers_ipv6:
- 2001:db8::1
- 2001:db8::2- Block traffic - Stop new connections to the node
- Deploy application - Update services/containers
- Unblock traffic - Re-enable load balancer connections
- name: Rolling deployment for application servers
hosts: app_servers
serial: "{{ serial_number | default(1) }}" # Deploy one node at a time
become: true
gather_facts: true
vars:
rolling_deploy_parent_servers_ipv4:
- 172.24.1.81
- 172.24.1.82
tasks:
- name: Block load balancer connections
import_role:
name: fccn.ansible_rolling_deploy
vars:
rolling_deploy_starting: true
when: groups['app_servers'] | length > 1
- name: Wait for active connections to drain
wait_for:
timeout: 30
# Your deployment tasks here
- name: Deploy application
docker_container:
name: myapp
image: myapp:latest
state: started
restart: true
- name: Verify application health
uri:
url: http://localhost:8080/health
status_code: 200
retries: 10
delay: 3
- name: Re-enable load balancer connections
import_role:
name: fccn.ansible_rolling_deploy
vars:
rolling_deploy_starting: false
when: groups['app_servers'] | length > 1- name: Simple rolling deployment
hosts: web_servers
serial: 1
become: true
tasks:
- name: Start rolling deploy
import_role:
name: fccn.ansible_rolling_deploy
vars:
rolling_deploy_starting: true
rolling_deploy_parent_servers_ipv4: ['172.24.1.81']
# ... your deployment tasks ...
- name: Finish rolling deploy
import_role:
name: fccn.ansible_rolling_deploy
vars:
rolling_deploy_starting: false
rolling_deploy_parent_servers_ipv4: ['172.24.1.81']-
Traffic Blocking Phase (
rolling_deploy_starting: true)- Inserts iptables rules at the top of FORWARD and INPUT chains
- Blocks incoming connections from specified load balancers
- Existing connections remain active
-
Deployment Phase
- Node is isolated from new traffic
- Safe to restart services or deploy updates
- Health checks can be performed in isolation
-
Traffic Restoration Phase (
rolling_deploy_starting: false)- Removes iptables blocking rules
- Node begins accepting new connections
- Load balancer resumes forwarding traffic
- FORWARD chain: Used for Docker containers with bridge networking
- INPUT chain: Used for non-containerized applications or host network mode
serial: 1 or small numbers to prevent deploying all nodes simultaneously:
- name: Deploy servers
hosts: app_servers
serial: "{{ serial_number | default(1) }}"
# ...This ensures at least some nodes remain available during the deployment.
Add a delay after blocking traffic to allow existing connections to complete:
- name: Wait for connections to drain
wait_for:
timeout: 30Always verify application health before re-enabling traffic:
- name: Verify service is ready
uri:
url: http://localhost:{{ app_port }}/health
status_code: 200
retries: 10
delay: 5Only use this role when you have multiple nodes (when: groups['servers'] | length > 1), otherwise you'll cause a complete outage.
# View IPv4 rules
sudo iptables -L -n -v
# View IPv6 rules
sudo ip6tables -L -n -vIf deployment fails and rules aren't cleaned up:
# Remove blocking rules
sudo iptables -D INPUT -s <load_balancer_ip> -j DROP
sudo iptables -D FORWARD -s <load_balancer_ip> -j DROPRun with verbose output:
ansible-playbook -i inventory playbook.yml -vvNone.
GNU General Public License v3.0
Created and maintained by Ivo Branco at FCCN.
Contributions are welcome! Please open an issue or submit a pull request.
Repository: fccn/ansible-rolling-deploy