Configuration Management with Ansible
Complete guide for managing SBM CRM Platform server configuration using Ansible.
Prerequisites
- Ansible >= 2.9
- SSH access to target servers
- Python 3 on target servers
Ansible Structure
ansible/
├── inventory/
│ ├── production
│ └── staging
├── group_vars/
│ ├── production
│ └── staging
├── host_vars/
│ └── api-01
├── roles/
│ ├── docker
│ ├── nodejs
│ ├── postgresql
│ └── nginx
└── playbooks/
├── site.yml
├── deploy.yml
└── update.yml
Inventory Configuration
inventory/production
[api_servers]
sbmcrm-api-01 ansible_host=10.0.1.10 ansible_user=ubuntu
sbmcrm-api-02 ansible_host=10.0.2.10 ansible_user=ubuntu
[api_servers:vars]
ansible_ssh_private_key_file=~/.ssh/sbmcrm-keypair
ansible_python_interpreter=/usr/bin/python3
inventory/staging
[api_servers]
sbmcrm-staging-api-01 ansible_host=10.1.1.10 ansible_user=ubuntu
[api_servers:vars]
ansible_ssh_private_key_file=~/.ssh/sbmcrm-staging-keypair
ansible_python_interpreter=/usr/bin/python3
Group Variables
group_vars/production
---
environment: production
app_name: sbmcrm-platform
app_version: latest
docker_compose_version: "2.20.0"
nodejs_version: "18"
database_host: "10.0.10.20"
database_name: "sbmcrm_production"
redis_host: "10.0.10.30"
ssl_cert_path: "/etc/ssl/certs/sbmcrm.crt"
ssl_key_path: "/etc/ssl/private/sbmcrm.key"
group_vars/staging
---
environment: staging
app_name: sbmcrm-platform
app_version: staging
database_host: "10.1.10.20"
database_name: "sbmcrm_staging"
redis_host: "10.1.10.30"
Roles
Role: docker
roles/docker/tasks/main.yml
---
- name: Install Docker dependencies
apt:
name:
- apt-transport-https
- ca-certificates
- curl
- gnupg
- lsb-release
state: present
update_cache: yes
- name: Add Docker GPG key
apt_key:
url: https://download.docker.com/linux/ubuntu/gpg
state: present
- name: Add Docker repository
apt_repository:
repo: "deb [arch=amd64] https://download.docker.com/linux/ubuntu {{ ansible_distribution_release }} stable"
state: present
- name: Install Docker
apt:
name:
- docker-ce
- docker-ce-cli
- containerd.io
state: present
- name: Install Docker Compose
get_url:
url: "https://github.com/docker/compose/releases/download/v{{ docker_compose_version }}/docker-compose-linux-x86_64"
dest: /usr/local/bin/docker-compose
mode: '0755'
- name: Start and enable Docker
systemd:
name: docker
state: started
enabled: yes
- name: Add user to docker group
user:
name: "{{ ansible_user }}"
groups: docker
append: yes
Role: nodejs
roles/nodejs/tasks/main.yml
---
- name: Install Node.js dependencies
apt:
name:
- curl
- build-essential
state: present
- name: Add NodeSource repository
shell: |
curl -fsSL https://deb.nodesource.com/setup_{{ nodejs_version }}.x | bash -
args:
creates: /etc/apt/sources.list.d/nodesource.list
- name: Install Node.js
apt:
name: nodejs
state: present
update_cache: yes
- name: Install global npm packages
npm:
name: "{{ item }}"
global: yes
loop:
- pm2
- nodemon
Role: nginx
roles/nginx/tasks/main.yml
---
- name: Install Nginx
apt:
name: nginx
state: present
- name: Configure Nginx
template:
src: nginx.conf.j2
dest: /etc/nginx/sites-available/{{ app_name }}
notify: restart nginx
- name: Enable site
file:
src: /etc/nginx/sites-available/{{ app_name }}
dest: /etc/nginx/sites-enabled/{{ app_name }}
state: link
notify: restart nginx
- name: Remove default site
file:
path: /etc/nginx/sites-enabled/default
state: absent
notify: restart nginx
- name: Start and enable Nginx
systemd:
name: nginx
state: started
enabled: yes
roles/nginx/templates/nginx.conf.j2
upstream sbmcrm_backend {
least_conn;
server 127.0.0.1:3000;
}
server {
listen 80;
server_name api.yourdomain.com;
return 301 https://$server_name$request_uri;
}
server {
listen 443 ssl http2;
server_name api.yourdomain.com;
ssl_certificate {{ ssl_cert_path }};
ssl_certificate_key {{ ssl_key_path }};
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
ssl_prefer_server_ciphers on;
location / {
proxy_pass http://sbmcrm_backend;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_cache_bypass $http_upgrade;
}
location /health {
proxy_pass http://sbmcrm_backend;
access_log off;
}
}
roles/nginx/handlers/main.yml
---
- name: restart nginx
systemd:
name: nginx
state: restarted
Playbooks
site.yml - Main Playbook
---
- name: Configure SBM CRM Platform servers
hosts: api_servers
become: yes
roles:
- docker
- nodejs
- nginx
tasks:
- name: Create application directory
file:
path: /opt/{{ app_name }}
state: directory
owner: "{{ ansible_user }}"
group: "{{ ansible_user }}"
mode: '0755'
- name: Copy docker-compose file
template:
src: docker-compose.yml.j2
dest: /opt/{{ app_name }}/docker-compose.yml
notify: restart application
- name: Copy environment file
template:
src: .env.j2
dest: /opt/{{ app_name }}/.env
notify: restart application
- name: Pull Docker images
docker_compose:
project_src: /opt/{{ app_name }}
pull: yes
- name: Start application
docker_compose:
project_src: /opt/{{ app_name }}
state: started
handlers:
- name: restart application
docker_compose:
project_src: /opt/{{ app_name }}
state: restarted
deploy.yml - Deployment Playbook
---
- name: Deploy SBM CRM Platform
hosts: api_servers
become: yes
tasks:
- name: Pull latest images
docker_compose:
project_src: /opt/{{ app_name }}
pull: yes
- name: Run database migrations
docker_compose:
project_src: /opt/{{ app_name }}
services:
- api
command: npm run migrate
- name: Restart application
docker_compose:
project_src: /opt/{{ app_name }}
state: restarted
- name: Wait for health check
uri:
url: http://localhost:3000/health
status_code: 200
register: result
until: result.status == 200
retries: 10
delay: 5
update.yml - Update Playbook
---
- name: Update SBM CRM Platform
hosts: api_servers
become: yes
tasks:
- name: Update system packages
apt:
update_cache: yes
upgrade: dist
autoremove: yes
- name: Update Docker images
docker_compose:
project_src: /opt/{{ app_name }}
pull: yes
images: yes
- name: Recreate containers
docker_compose:
project_src: /opt/{{ app_name }}
state: present
recreate: yes
Usage
Run Playbook
# Production
ansible-playbook -i inventory/production playbooks/site.yml
# Staging
ansible-playbook -i inventory/staging playbooks/site.yml
Deploy Application
ansible-playbook -i inventory/production playbooks/deploy.yml
Update System
ansible-playbook -i inventory/production playbooks/update.yml
Ad-Hoc Commands
# Check server status
ansible api_servers -i inventory/production -m shell -a "docker ps"
# View logs
ansible api_servers -i inventory/production -m shell -a "docker-compose logs api"
# Restart services
ansible api_servers -i inventory/production -m shell -a "docker-compose restart" -b
Best Practices
- Idempotency: Ensure all tasks are idempotent
- Variables: Use variables for configuration
- Roles: Organize tasks into reusable roles
- Templates: Use Jinja2 templates for configuration files
- Vault: Use Ansible Vault for sensitive data
- Testing: Test playbooks in staging first
- Documentation: Document all roles and playbooks
Next Steps
- Review Terraform for infrastructure provisioning
- Configure High Availability for production