Skip to content
← IaC · প্রাথমিক · 12 মিনিট · 02 / 05 EN

Ansible Fundamentals

Inventory, playbook, role, আর variable — কোনো agent install না করেই নির্ভরযোগ্যভাবে server configure করা।

Ansibleplaybooksrolesinventoryhandlerstemplates

বাস্তব জীবনের উদাহরণ

script হাতে একজন stage director: তিনি প্রতিটা কাজ নিজে করেন না — তিনি সঠিক actor-দের (host) সঠিক ক্রমে (task) নির্দেশ দেন, আর ভিন্ন character-কে ভিন্নভাবে সামলানোর নমনীয়তা রাখেন (variable আর conditional)। Ansible হলো director; আপনার server-গুলো হলো cast।

গল্পে বুঝি

ইবনে সিনার একটা নামকরা রেস্তোরাঁর চেইন — শহরের নানা মহল্লায় ছড়ানো অনেকগুলো শাখার রান্নাঘর, প্রতিটাই আগে থেকে চালু। সমস্যা হলো, প্রতিটা শাখার রান্না একেক রকম হয়ে যাচ্ছিল। তাই ইবনে সিনা একটা লিখিত নির্দেশনা-শিট বানালেন, ধাপে ধাপে সাজানো: “১. কাউন্টার ঘষে পরিষ্কার করো, ২. মসলার তাক ভরাও, ৩. চুলা জ্বালাও”। তারপর একজন দৌড়বিদ ছেলেকে দিয়ে প্রতিটা শাখায় হুবহু একই শিট পাঠিয়ে দিলেন। শাখায় কোনো স্থায়ী লোক বসিয়ে রাখতে হয়নি — দৌড়বিদ শুধু শিটটা পৌঁছে দিয়ে রাঁধুনিকে ধাপগুলো ধরিয়ে দিয়ে আসে।

মজার ব্যাপার হলো, কোনো ধাপ যদি আগে থেকেই করা থাকে — ধরুন মসলার তাক আগেই ভরা — রাঁধুনি সেটা টুক করে টপকে যায়, নতুন করে কিছু বদলায় না। তাই একই শিট আজ পাঠান, কাল পাঠান, পরশু পাঠান — কোনো ক্ষতি নেই; যেটুকু বাকি সেটুকুই ঠিক হয়, বাকিটা যেমন আছে তেমনই থাকে। আল-খোয়ারিজমি চাইলে শুধু নতুন খোলা শাখাটায়ও একই শিট পাঠিয়ে সেটাকে সবার সাথে মিলিয়ে দিতে পারেন।

এই গল্পটাই আসলে Ansible। ধাপে ধাপে সাজানো নির্দেশনা-শিটটাই হলো একটা playbook, যার প্রতিটা লাইন একেকটা task। শিটটা চালু শাখাগুলোয় পাঠানোটাই push-based config — Ansible আপনার control node থেকে চালু server-গুলোতে SSH দিয়ে গিয়ে task চালায়। দৌড়বিদের জন্য শাখায় স্থায়ী কেউ লাগে না — এটাই agentless, target-এ কোনো daemon বসাতে হয় না। “আগে থেকে করা ধাপ টপকে যাও, কিছু বদলায় না” — এটাই idempotency, একই playbook বারবার চালালেও শুধু যা desired state-এ নেই সেটুকুই বদলায়। আর কোন কোন শাখায় শিট যাবে তার তালিকাটাই inventory। বাস্তবে ঠিক এভাবেই টিমগুলো Ansible দিয়ে ডজন ডজন চালু server-কে একই কনফিগে standardize করে — নতুন কোনো agent install না করেই।

Ansible কীভাবে কাজ করে

Ansible agentless — এটা target মেশিনে SSH করে আর task চালায়। Managed node-এ কোনো daemon install করতে হয় না। প্রয়োজন: control node থেকে SSH access, target-এ Python।

Control node (your laptop or CI server)
  → SSH to managed nodes
  → Copies and runs Python modules
  → Reports results back
  → Applies changes to reach desired state

Inventory

Inventory define করে Ansible কোন host manage করে আর কীভাবে সেগুলোতে পৌঁছায়:

# inventory/hosts.ini

[web]
web-1.example.com
web-2.example.com ansible_user=ubuntu

[db]
db-primary.example.com ansible_user=postgres ansible_port=2222

[all:vars]
ansible_user=admin
ansible_ssh_private_key_file=~/.ssh/deploy_key

[production:children]
web
db

Dynamic inventory (cloud environment-এর জন্য — scale করার সাথে host বদলায়):

# AWS dynamic inventory
ansible-inventory -i aws_ec2.yml --list

# aws_ec2.yml
plugin: aws_ec2
regions:
  - us-east-1
filters:
  instance-state-name: running
  tag:Environment: production
keyed_groups:
  - key: tags.Role     # group by Role tag: [tag_Role_web], [tag_Role_db]

Playbook

একটা playbook হলো play-এর একটা list। প্রতিটা play একদল host-এ task apply করে।

# site.yml
---
- name: Configure web servers
  hosts: web
  become: true # sudo
  vars:
    app_port: 3000
    nginx_worker_processes: auto

  tasks:
    - name: Update apt cache
      apt:
        update_cache: true
        cache_valid_time: 3600 # only update if cache is > 1hr old

    - name: Install packages
      apt:
        name:
          - nginx
          - certbot
          - python3-certbot-nginx
        state: present

    - name: Create app user
      user:
        name: appuser
        system: true
        shell: /usr/sbin/nologin

    - name: Deploy nginx config
      template:
        src: templates/nginx.conf.j2
        dest: /etc/nginx/sites-available/myapp
        owner: root
        group: root
        mode: '0644'
      notify: Reload nginx # triggers handler only if this task changed something

    - name: Enable nginx site
      file:
        src: /etc/nginx/sites-available/myapp
        dest: /etc/nginx/sites-enabled/myapp
        state: link

  handlers:
    - name: Reload nginx
      service:
        name: nginx
        state: reloaded # reload (not restart) — avoids dropping connections
# Run the playbook
ansible-playbook -i inventory/hosts.ini site.yml

# Dry run — show what would change
ansible-playbook -i inventory/hosts.ini site.yml --check --diff

# Limit to specific hosts
ansible-playbook -i inventory/hosts.ini site.yml --limit web-1.example.com

# Run specific tags
ansible-playbook -i inventory/hosts.ini site.yml --tags nginx

Template (Jinja2)

Template দিয়ে আপনি variable সহ config file generate করতে পারেন:

{# templates/nginx.conf.j2 #}
upstream app {
    {% for i in range(app_instances) %}
    server 127.0.0.1:{{ app_port + i }};
    {% endfor %}
}

server {
    listen 80;
    server_name {{ domain_name }};

    location / {
        proxy_pass http://app;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
    }

    access_log /var/log/nginx/{{ app_name }}.access.log;
    error_log  /var/log/nginx/{{ app_name }}.error.log;
}

Variable আসে: playbook vars, inventory vars, group_vars, host_vars থেকে, কিংবা runtime-এ পাস করা হয়।

Variable আর Precedence

# group_vars/web.yml — applies to all hosts in [web] group
app_port: 3000
nginx_worker_processes: auto
log_level: warn

# group_vars/production.yml — overrides for production
log_level: error

# host_vars/web-1.example.com.yml — overrides for specific host
nginx_worker_connections: 2048

Ansible variable precedence (ছোট নম্বর = কম priority, বড় নম্বর দিয়ে override হয়):

1. Role defaults
2. Inventory vars
3. group_vars
4. host_vars
5. Play vars
6. Task vars (highest — use sparingly)

Role

Role হলো পুনর্ব্যবহারযোগ্য, স্বয়ংসম্পূর্ণ Ansible content-এর unit। একটা বিশাল playbook-এর বদলে role task-গুলোকে function অনুযায়ী সাজায়।

roles/
  nginx/
    tasks/
      main.yml        # entry point
      install.yml
      configure.yml
    handlers/
      main.yml
    templates/
      nginx.conf.j2
      vhost.conf.j2
    defaults/
      main.yml        # default variable values
    vars/
      main.yml        # role-specific variables (higher precedence than defaults)
    files/
      dhparam.pem     # static files to copy
# roles/nginx/tasks/main.yml
---
- import_tasks: install.yml
- import_tasks: configure.yml

# roles/nginx/tasks/install.yml
---
- name: Install nginx
  apt:
    name: nginx
    state: present

# roles/nginx/defaults/main.yml
---
nginx_worker_processes: auto
nginx_worker_connections: 1024
nginx_keepalive_timeout: 65
# Use roles in a playbook
- name: Configure web servers
  hosts: web
  become: true
  roles:
    - nginx
    - { role: certbot, domain: myapp.com }
    - node_app

Handler

Handler কেবল notify হলেই চলে — আর একবারই, play-এর শেষে, একাধিকবার notify হলেও:

tasks:
  - name: Deploy nginx.conf
    template:
      src: nginx.conf.j2
      dest: /etc/nginx/nginx.conf
    notify: Reload nginx

  - name: Deploy vhost config
    template:
      src: vhost.conf.j2
      dest: /etc/nginx/sites-available/myapp
    notify: Reload nginx # notified twice, but handler runs once

handlers:
  - name: Reload nginx
    service:
      name: nginx
      state: reloaded

যদি কোনো task-ই কিছু পরিবর্তন না করে (কারণ config আগে থেকেই সঠিক), তাহলে handler কখনো চলে না। এটাই idempotency বাস্তবে।

Ansible Vault দিয়ে Secrets

কখনো plaintext YAML-এ password বা API key রাখবেন না:

# Encrypt a file
ansible-vault encrypt group_vars/production/secrets.yml

# Edit encrypted file
ansible-vault edit group_vars/production/secrets.yml

# Run playbook with vault password
ansible-playbook site.yml --vault-password-file ~/.vault_pass
# or
ansible-playbook site.yml --ask-vault-pass
# group_vars/production/secrets.yml (encrypted at rest)
db_password: '{{ vault_db_password }}'
api_key: '{{ vault_api_key }}'

# group_vars/production/vars.yml (plaintext, references vault vars)
database_url: 'postgresql://app:{{ db_password }}@db.internal/mydb'

vault_pass CI secrets-এ রাখুন, repo-তে নয়। Encrypted file-গুলো নিরাপদে commit করা যায়।

একটা সম্পূর্ণ Server Provisioning উদাহরণ

# provision-web-server.yml
---
- name: Provision web server
  hosts: web
  become: true

  vars_files:
    - group_vars/all/secrets.yml

  roles:
    - common # base packages, users, sshd config, ufw
    - nginx # install, configure
    - node # install node via nvm
    - app # deploy application, systemd unit

  post_tasks:
    - name: Verify app is running
      uri:
        url: 'http://localhost:3000/health'
        status_code: 200
      retries: 5
      delay: 5

একটা নতুন server provision হওয়ার পর সেটার ওপর চালান:

ansible-playbook \
  -i "newserver.example.com," \  # comma = treat as list, not file
  provision-web-server.yml \
  --private-key ~/.ssh/deploy_key

যে playbook একটা নতুন server configure করে সেটাই পরে আবার চালিয়ে config পরিবর্তন apply করা বা application আপডেট করা যায়। একেবারে নিচ পর্যন্ত idempotent।