Skip to main content

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

  1. Idempotency: Ensure all tasks are idempotent
  2. Variables: Use variables for configuration
  3. Roles: Organize tasks into reusable roles
  4. Templates: Use Jinja2 templates for configuration files
  5. Vault: Use Ansible Vault for sensitive data
  6. Testing: Test playbooks in staging first
  7. Documentation: Document all roles and playbooks

Next Steps

  1. Review Terraform for infrastructure provisioning
  2. Configure High Availability for production