Ansible Fundamentals
Inventory, playbook, role, আর variable — কোনো agent install না করেই নির্ভরযোগ্যভাবে server configure করা।
বাস্তব জীবনের উদাহরণ
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।