OPS · PRODUCTION DEPLOYMENT

Ansible — استقرار خودکار PlayPulse روی سرور Ubuntu

← بازگشت به خانه · راهنمای انگلیسی کوتاه: ansible/README.md


این صفحه مرجع کامل استقرار production با Ansible است: فلسفهٔ طراحی، ساختار پوشه، نقش هر فایل، جریان اجرای playbook، تضمین idempotency، راهنمای گام‌به‌گام برای سرور واقعی، و تمام دستورها و سوئیچ‌های موردنیاز این پروژه. پس از خواندن باید بتوانید بدون حدس زدن، از صفر تا استقرار کامل و به‌روزرسانی‌های بعدی را اجرا کنید.

فهرست:

  1. فلسفه و جایگاه Ansible در پروژه
  2. ساختار پوشهٔ ansible/
  3. نقش‌ها (Roles) و کار هر کدام
  4. جریان کامل یک Deploy
  5. Idempotency — چرا اجرای دوباره امن است
  6. متغیرها، Vault و قالب .env
  7. راهنمای گام‌به‌گام Production
  8. مرجع دستورها، تگ‌ها و سوئیچ‌ها
  9. پیوند با اسکریپت‌های پروژه
  10. چک‌لیست و نکات عملیاتی

۱. فلسفه و جایگاه Ansible در پروژه

Ansible اینجا یک لایهٔ نازک روی ابزار خود پروژه است، نه جایگزین Compose یا منطق راه‌اندازی. منطق واقعی «بالا آوردن استک» داخل اسکریپت‌های مخزن است:

کار Ansible این است که روی سرور Ubuntu خام: Docker را نصب کند، مخزن را clone/pull کند، فایل .env را از قالب و Vault بسازد، سپس همان اسکریپت‌ها را صدا بزند. اگر فردا منطق bring_up عوض شود، playbook عمداً آن را کپی نمی‌کند — فقط همان اسکریپت را اجرا می‌کند.

مرز مسئولیت:

لایهمسئولیتچه چیزی نیست
Ansible Provisioning سرور + تحویل کد + رندر .env + فراخوانی اسکریپت‌ها بازنویسی docker compose یا منطق seed/crawl
اسکریپت‌های scripts/ راه‌اندازی استک، seed، smoke، تست ایزوله نصب Docker روی OS یا مدیریت SSH
Compose / سرویس‌ها تعریف کانتینرها، healthcheck، پروفایل‌ها دانستن اینکه اپراتور کی deploy می‌زند

هدف سرور: production Ubuntu با SSH و کاربر sudo. Control node (لپ‌تاپ یا CI) فقط Ansible را دارد؛ روی خود سرور لازم نیست Ansible نصب باشد.


۲. ساختار پوشهٔ ansible/

ansible/
  ansible.cfg                 # تنظیمات پیش‌فرض (inventory، roles_path، become، diff)
  inventory.ini.example       # نمونهٔ inventory — کپی به inventory.ini
  playbook.yml                # play اصلی: docker → project_setup → deploy
  group_vars/
    all.yml                   # متغیرهای غیرحساس (قابل commit)
    vault.yml.example         # قالب رمزها — فایل واقعی vault.yml رمزنگاری‌شده و gitignore
  roles/
    docker/tasks/main.yml
    project_setup/tasks/main.yml
    deploy/tasks/main.yml
  templates/
    env.j2                    # Jinja2 برای .env سرور (آینهٔ .env.example ریشه)
  README.md                   # خلاصهٔ انگلیسی
مسیرنقش
ansible.cfg inventory پیش‌فرض inventory.ini، مسیر roles، host_key_checking=False برای اولین اتصال، همیشه نمایش diff برای قالب‌ها، و become=True (sudo به root).
inventory.ini.example گروه [production] با placeholder IP. فایل واقعی inventory.ini gitignore است و نباید commit شود.
playbook.yml تعریف play روی hosts گروه production؛ بارگذاری Vault؛ ترتیب سه role.
group_vars/all.yml URL مخزن، مسیر نصب روی سرور، پورت‌ها، فلگ گزارش چرخه، run_tests_before_deploy.
group_vars/vault.yml رمز Postgres (db/user/password). فقط با ansible-vault ساخته می‌شود؛ در git نیست.
templates/env.j2 خروجی: {{ project_dir }}/.env با mode 0600 و backup زمان‌دار هنگام تغییر.
roles/* هر role یک مسئولیت شفاف؛ تگ‌گذاری برای اجرای جزئی.

۳. نقش‌ها (Roles) و کار هر کدام

۳.۱ نقش docker — تگ docker

نصب idempotent موتور Docker و پلاگین Compose از مخزن رسمی APT داکر:

  1. پیش‌نیازهای apt (ca-certificates, curl, gnupg)
  2. کلید GPG و ریپازیتوری Docker برای Ubuntu هدف
  3. نصب docker-ce، CLI، containerd، buildx، docker-compose-plugin
  4. سرویس docker: started + enabled
  5. افزودن کاربر SSH (ansible_user) به گروه docker تا بعداً بدون sudo بتواند docker بزند

اجرای دوم معمولاً فقط ok می‌دهد و چیزی نصب دوباره نمی‌کند.

۳.۲ نقش project_setup — تگ‌های project_setup و بخشی deploy

  1. اطمینان از نصب git
  2. ساخت دایرکتوری project_dir (پیش‌فرض /opt/playpulse)
  3. git clone یا pull روی project_version (پیش‌فرض main) — با force: false تا تغییرات محلی ناخواسته پاک نشوند
  4. رندر templates/env.j2 → {{ project_dir }}/.env

تسک‌های clone و template علاوه بر project_setup تگ deploy هم دارند؛ بنابراین --tags deploy کد و .env را به‌روز می‌کند بدون دست زدن به نصب Docker.

۳.۳ نقش deploy — تگ‌های deploy و tests

ترتیب ثابت داخل این role:

گامدستوررفتار شکست
۰ (اختیاری) ./scripts/run_tests_in_docker.sh فقط وقتی run_tests_before_deploy=true. با failed_when: false کل playbook را نمی‌شکند؛ خروجی و در صورت rc≠۰ یک Warning چاپ می‌شود.
۱ ./scripts/bring_up.sh --skip-check شکست اسکریپت → شکست play. --skip-check تا smoke جدا و شفاف در گام بعد باشد.
۲ ./scripts/infra/check_infra.sh rc≠۰ → شکست play (استک «بالا آمده اما سالم نیست» پذیرفته نمی‌شود).

خروجی هر اسکریپت با تسک debug در لاگ Ansible دیده می‌شود تا در CI یا ترمینال، لاگ bring_up / تست / smoke کامل باشد.


۴. جریان کامل یک Deploy

اجرای کامل پیش‌فرض (run_tests_before_deploy=false):

Control node                     Target Ubuntu server
─────────────                    ────────────────────
ansible-playbook ...
        │
        ├─ role: docker ────────► نصب/تأیید Docker + Compose + گروه docker
        │
        ├─ role: project_setup ► git clone/pull در /opt/playpulse
        │                        رندر .env از Vault + all.yml
        │
        └─ role: deploy
             ├─ (tests skipped)
             ├─ bring_up.sh --skip-check
             │     infra → app-api healthy → seed_apps.sh → crawler/consumer …
             └─ check_infra.sh  (Postgres / Kafka / Metabase)

با تست ایزوله قبل از bring_up (-e run_tests_before_deploy=true):

  1. همان docker + project_setup
  2. سپس روی سرور: استک جدا playpulse_test + .env.test (دست به root .env و استک live نمی‌زند)
  3. خلاصهٔ PASS/FAIL/SKIPPED چاپ می‌شود
  4. بعد bring_up و check_infra — حتی اگر تست soft-fail شده باشد (تصمیم نهایی با اپراتور)

چرا تست قبل از bring_up؟ سوئیت ایزوله استک live را verify نمی‌کند؛ health واقعی بعد از bring_up با check_infra است. اجرای تست قبل از بالا آوردن سرویس‌های production از رقابت منابع با crawler/Metabase جلوگیری می‌کند و مشکل واضح کد/ماشین را زودتر نشان می‌دهد.

داخل bring_up.sh (خلاصه — جزئیات در مستند bring_up):

  1. زیرساخت: postgres، zookeeper، kafka، kafka-init، kafka-ui، metabase
  2. فقط app-api تا healthy
  3. seed_apps.sh — ثبت ۱۵ اپ اولیه
  4. بقیه: storage-consumer، crawler (+ اختیاری cycle-reporter)
  5. sentiment فقط اگر image از قبل باشد؛ بدون cold-build

۵. Idempotency — چرا اجرای دوباره امن است

Idempotent یعنی بار دوم و سوم همان playbook نباید سیستم را خراب کند یا state متناقض بسازد. در این پروژه لایه‌لایه تضمین شده:

جزءچگونه idempotent استعلامت در Ansible
نصب Docker (apt / key / repo / service) ماژول‌های رسمی؛ اگر بسته نصب باشد دوباره نصب سنگین نمی‌شود اجرای دوم: بیشتر ok، نه changed
git module اگر dest موجود باشد update می‌کند؛ force: false changed فقط وقتی ref عوض شود
قالب .env فقط وقتی محتوای رندرشده فرق کند می‌نویسد؛ backup زمان‌دار changed + diff در خروجی
bring_up.sh / Compose docker compose up -d و seed با HTTP 409 برای duplicate Ansible تسک command را معمولاً changed می‌زند (چون shell است)، ولی اثر جانبی روی سرور idempotent است
check_infra.sh فقط read/write تستی موقت و پاکسازی changed_when: false
تست ایزوله Docker پروژهٔ جدا + teardown volume در پایان changed_when: false؛ استک live دست‌نخورده

نکتهٔ ذهنی مهم: «Ansible گفت changed» برای تسک‌های command مربوط به bring_up به‌معنی «سرور خراب شد» نیست؛ یعنی «اسکریپت دوباره اجرا شد». سلامت واقعی را از خروجی اسکریپت و موفقیت check_infra بخوانید.


۶. متغیرها، Vault و قالب .env

۶.۱ group_vars/all.yml (غیرحساس — commit می‌شود)

متغیرپیش‌فرض مفهومیکاربرد
project_repo_urlURL گیت‌هاب PlayPulseمنبع clone/pull
project_versionmainbranch / tag / commit
project_dir/opt/playpulseمسیر کار روی سرور
postgres_port / app_api_port / …مطابق .env.exampleپورت‌های publish در .env
crawler_cycle_reports_enabledfalseگزارش چرخه crawl+persist
run_tests_before_deployfalseاجرای اختیاری تست ایزوله قبل از bring_up

۶.۲ Vault (حساس — commit نمی‌شود)

کلیدهای لازم در vault.yml:

  • vault_postgres_db
  • vault_postgres_user
  • vault_postgres_password

این‌ها داخل env.j2 به POSTGRES_* تبدیل می‌شوند. playbook فایل Vault را با vars_files صریح لود می‌کند؛ برای decrypt هنگام اجرا معمولاً --ask-vault-pass لازم است.

۶.۳ خروجی .env روی سرور

فقط برای substitution در docker-compose.yml است (اعتبارنامه و پورت‌ها). فایل با mode 0600 نوشته می‌شود. ویرایش دستی روی سرور در deploy بعدی با رندر قالب ممکن است overwrite شود — منبع حقیقت Vault + all.yml است.


۷. راهنمای گام‌به‌گام Production

گام ۰ — پیش‌نیازها

  • یک سرور Ubuntu با IP عمومی/خصوصی قابل SSH
  • کاربر با sudo (مثلاً ubuntu)
  • روی لپ‌تاپ/CI: Ansible نصب باشد (ansible --version)
  • مخزن PlayPulse روی control node کلون شده باشد تا پوشهٔ ansible/ در دسترس باشد

گام ۱ — نصب Ansible روی control node

# Ubuntu/Debian
sudo apt update && sudo apt install -y ansible

# یا با pipx
pipx install --include-deps ansible

ansible --version

گام ۲ — Inventory واقعی

cd ansible
cp inventory.ini.example inventory.ini
# ویرایش: ansible_host = IP واقعی سرور، ansible_user = کاربر SSH

نمونهٔ مفهومی:

[production]
prod-server ansible_host=203.0.113.10

[production:vars]
ansible_user=ubuntu
# ansible_ssh_private_key_file=~/.ssh/id_ed25519
ansible_python_interpreter=/usr/bin/python3

تست اتصال:

ansible -i inventory.ini production -m ping

گام ۳ — ساخت Vault رمزنگاری‌شده

cd ansible
ansible-vault create group_vars/vault.yml
# پسورد Vault را تنظیم کنید و این کلیدها را با مقادیر واقعی production بگذارید:
#
# vault_postgres_db: "playpulse_prod"
# vault_postgres_user: "playpulse"
# vault_postgres_password: "a-long-random-secret"

بعداً:

ansible-vault edit group_vars/vault.yml
ansible-vault view group_vars/vault.yml

گام ۴ — تنظیمات غیرحساس (در صورت نیاز)

در group_vars/all.yml مثلاً project_version را روی tag انتشار بگذارید، یا پورت‌ها را با سیاست فایروال هم‌تراز کنید. برای deploy عادی run_tests_before_deploy را false بگذارید.

گام ۵ — اولین استقرار کامل

cd ansible
ansible-playbook -i inventory.ini playbook.yml --ask-vault-pass
# اگر sudo روی سرور پسورد می‌خواهد:
ansible-playbook -i inventory.ini playbook.yml --ask-vault-pass -K

انتظار: نصب Docker (اولین بار طولانی‌تر)، clone، نوشتن .env، bring_up مرحله‌ای، seed اپ‌ها، سپس check_infra سبز. URLهای مفید روی سرور: App API روی APP_API_PORT، Kafka UI، Metabase.

گام ۶ — به‌روزرسانی بعدی کد (بدون نصب دوباره Docker)

ansible-playbook -i inventory.ini playbook.yml --tags deploy --ask-vault-pass

این مسیر: pull + رندر .env + (اختیاری تست) + bring_up + check_infra.

گام ۷ — Deploy با تست ایزوله قبل از bring_up

ansible-playbook -i inventory.ini playbook.yml --ask-vault-pass \
  -e "run_tests_before_deploy=true"

خلاصهٔ PASS/FAIL/SKIPPED را در خروجی debug بخوانید. SKIPPED برای sentiment بدون image یا محدودیت محیطی live/oracle طبیعی است؛ هر FAIL واقعی را قبل از اعتماد به سرور بررسی کنید. این گام playbook را به‌خاطر soft-fail متوقف نمی‌کند.


۸. مرجع دستورها، تگ‌ها و سوئیچ‌ها

۸.۱ تگ‌های playbook این پروژه

تگچه چیزی اجرا می‌شود
dockerفقط نقش نصب Docker/Compose
project_setupgit + ساخت مسیر + (با تگ کامل‌تر) رندر env — مطابق تگ‌های تسک‌ها
deployبه‌روزرسانی کد + .env + مسیر deploy (تست اختیاری + bring_up + check_infra)
testsفقط بلوک تست ایزوله؛ همچنان نیاز به -e run_tests_before_deploy=true

۸.۲ دستورهای رایج

cd ansible

# استقرار کامل
ansible-playbook -i inventory.ini playbook.yml --ask-vault-pass

# استقرار کامل + sudo password
ansible-playbook -i inventory.ini playbook.yml --ask-vault-pass -K

# فقط redeploy (بدون نقش docker)
ansible-playbook -i inventory.ini playbook.yml --tags deploy --ask-vault-pass

# فقط Docker
ansible-playbook -i inventory.ini playbook.yml --tags docker --ask-vault-pass

# فقط تست ایزوله (روی سرور، قبل از/بدون bring_up در همین تگ)
ansible-playbook -i inventory.ini playbook.yml --tags tests --ask-vault-pass \
  -e "run_tests_before_deploy=true"

# فعال‌سازی گزارش چرخه از طریق متغیر (در all.yml یا -e)
ansible-playbook -i inventory.ini playbook.yml --ask-vault-pass \
  -e "crawler_cycle_reports_enabled=true"

# شاخه/تگ مشخص برای deploy
ansible-playbook -i inventory.ini playbook.yml --tags deploy --ask-vault-pass \
  -e "project_version=v1.2.3"

# اعتبارسنجی بدون تغییر
ansible-playbook -i inventory.ini playbook.yml --syntax-check
ansible-playbook -i inventory.ini playbook.yml --check --diff --ask-vault-pass

# لیست تسک‌ها قبل از اجرا
ansible-playbook -i inventory.ini playbook.yml --list-tasks
ansible-playbook -i inventory.ini playbook.yml --list-tags

۸.۳ سوئیچ‌های Ansible که در این پروژه زیاد به کار می‌آیند

سوئیچمعنا برای این پروژه
-i inventory.iniفایل hosts؛ اگر از داخل ansible/ اجرا کنید معمولاً از cfg هم خوانده می‌شود
--ask-vault-pass / -Jرمزگشایی group_vars/vault.yml
-K / --ask-become-passپسورد sudo روی سرور
--tags / --skip-tagsاجرای جزئی نقش‌ها
-e KEY=VALoverride متغیر (مثل تست یا version)
--checkdry-run؛ بعضی تسک‌های command ممکن است کامل شبیه‌سازی نشوند
--diffنمایش diff قالب‌ها؛ در cfg این پروژه diff همیشه روشن است
--limit hostnameاگر چند host در inventory دارید، محدود کردن هدف
-v / -vvجزئیات بیشتر برای دیباگ

۸.۴ دستورهای کمکی روی خود سرور (بعد از deploy)

cd /opt/playpulse   # یا project_dir شما

docker compose ps -a
./scripts/infra/check_infra.sh
./scripts/seed_apps.sh          # معمولاً لازم نیست؛ bring_up زده
curl -sS "http://localhost:${APP_API_PORT:-8000}/api/applications/" | python3 -m json.tool
docker compose logs crawler | grep "Crawl cycle summary"

۹. پیوند با اسکریپت‌های پروژه

اسکریپتمستندنقش در Ansible
bring_up.sh مستند هستهٔ بالا آوردن استک live؛ با --skip-check
seed_apps.sh مستند غیرمستقیم داخل bring_up، قبل از crawler
check_infra.sh مستند گام اجباری سلامت بعد از bring_up
run_tests_in_docker.sh مستند اختیاری قبل از bring_up؛ جایگزین health live نیست

۱۰. چک‌لیست و نکات عملیاتی

  • هرگز inventory.ini یا group_vars/vault.yml را commit نکنید.
  • اولین اتصال: از control node ansible … -m ping باید pong بدهد.
  • بعد از اولین Docker install، اگر کاربر تازه‌وارد گروه docker شده، برای session دستی SSH ممکن است یکبار logout/login لازم باشد؛ خود playbook با become/root اسکریپت‌ها را اجرا می‌کند.
  • تغییر رمز Postgres در Vault روی volume قدیمی دیتابیس موجود را «جادویی» عوض نمی‌کند — سیاست migrate/rotate جداگانه است؛ Vault برای رندر env و استقرارهای تازه/هم‌تراز است.
  • اگر git روی سرور به‌خاطر تغییر محلی fail شد (force: false)، عمدی است؛ دستی تصمیم بگیرید، نه force کور.
  • تست ایزوله و استک live می‌توانند هم‌زمان روی یک ماشین باشند، ولی در playbook تست را قبل از bring_up می‌گذاریم تا تداخل کمتر شود.
  • برای درک جزئیات Compose و seed، این صفحه را همراه bring_up و فهرست اسکریپت‌ها بخوانید.

جمع‌بندی یک خطی: Ansible سرور را آماده و کد/.env را تحویل می‌دهد؛ اسکریپت‌های پروژه استک را درست و idempotent بالا می‌آورند؛ check_infra مهر سلامت می‌زند؛ تست Docker اختیاری و جدا از استک live است.


← خانه · bring_up.sh · run_tests_in_docker.sh · اسکریپت‌ها