بلاگ/راه‌اندازی MongoDB Replica Set روی سرور مجازی و اختصاصی: دسترس‌پذیری بالا و Failover خودکار
به‌روز شده

راه‌اندازی MongoDB Replica Set روی سرور مجازی و اختصاصی: دسترس‌پذیری بالا و Failover خودکار

1405/08/120 بازدید
راه‌اندازی MongoDB Replica Set روی سرور مجازی و اختصاصی: دسترس‌پذیری بالا و Failover خودکار

راه‌اندازی MongoDB Replica Set روی سرور مجازی و اختصاصی: دسترس‌پذیری بالا و Failover خودکار

سه سرور به‌عنوان اعضای یک MongoDB Replica Set با یک Primary و دو Secondary

اگر MongoDB روی یک سرور تنها اجرا شود، هر ریبوت، کرش یا به‌روزرسانی برنامه را برای مدتی از کار می‌اندازد. راه‌اندازی MongoDB Replica Set همین مشکل را حل می‌کند: داده روی چند سرور کپی می‌شود و اگر یکی از عضوها از دسترس خارج شود، یکی دیگر خودکار جای آن را می‌گیرد.

در این راهنما یک Replica Set سه‌عضوی روی سه سرور مجازی یا اختصاصی جدا می‌سازیم. از آماده‌سازی شبکه و فایروال شروع می‌کنیم، نصب و پیکربندی را روی هر سه سرور انجام می‌دهیم، احراز هویت بین اعضا را با keyfile فعال می‌کنیم و در پایان Failover خودکار را با از کار انداختن Primary تست می‌کنیم.

Replica Set در MongoDB چطور کار می‌کند؟

یک Replica Set چند نسخه از همان دیتابیس را روی سرورهای مختلف نگه می‌دارد. یکی از اعضا در هر لحظه Primary است و تمام نوشتن‌ها را می‌پذیرد؛ بقیه اعضا Secondary هستند و تغییرات را از روی oplog (لاگ عملیات) Primary کپی می‌کنند.

اگر Primary از دسترس خارج شود، اعضای باقی‌مانده با رای‌گیری یک Secondary را به Primary جدید تبدیل می‌کنند. این فرایند election نام دارد و معمولا چند ثانیه طول می‌کشد، نه چند دقیقه. برنامه‌ای که با درایور رسمی MongoDB و retryWrites=true به کلاستر وصل است، همین چند ثانیه قطعی را به‌صورت خودکار دوباره تلاش می‌کند.

برای این‌که election همیشه نتیجه قطعی داشته باشد، تعداد اعضای رای‌دهنده باید فرد باشد. همین نکته معماری سه‌عضوی را به ساده‌ترین گزینه قابل‌اعتماد تبدیل می‌کند؛ با دو عضو، در صورت قطع شبکه بین آن دو، هیچ‌کدام نمی‌داند کدام باید Primary بماند.

معماری پیشنهادی: سه سرور، سه نقش

ساده‌ترین و پایدارترین حالت PSS است: یک Primary و دو Secondary با داده کامل. گزینه دیگر PSA نام دارد؛ یک Primary، یک Secondary و یک Arbiter که فقط در رای‌گیری شرکت می‌کند و کپی از داده نگه نمی‌دارد.

توپولوژیاعضای دارای داده کاملمنابع سرور سوممناسب برای
PSS (Primary-Secondary-Secondary)هر سه عضوباید منابع مشابه دو عضو دیگر داشته باشدمحیط عملیاتی، نیاز به خوانش از چند Secondary
PSA (Primary-Secondary-Arbiter)دو عضو اولسرور بسیار کوچک کافی استمحیط آزمایشی یا بودجه محدود

برای یک سرویس واقعی PSS را انتخاب کنید؛ چون اگر Primary در PSA از کار بیفتد، فقط یک نسخه کامل از داده باقی می‌ماند و آرایش دیگر در برابر خرابی دومین سرور محافظتی ندارد. در ادامه همین مقاله روی PSS پیش می‌رویم.

درباره انتخاب سرور: برای یک کلاستر دیتابیس عملیاتی با بار نوشتن زیاد یا نیاز به دیسک NVMe اختصاصی، سرور اختصاصی گزینه پایدارتری است؛ راهنمای خرید سرور اختصاصی برای دیتابیس جدول رم و دیسک پیشنهادی را دارد. برای محیط آزمایشی، پروژه کوچک یا شروع سریع، سه سرور مجازی هزینه بسیار کمتری دارد و بعدا هم می‌توان اعضا را یکی‌یکی به سرور قوی‌تر منتقل کرد؛ تفاوت دو مسیر را راهنمای انتخاب بین سرور مجازی و سرور اختصاصی کامل‌تر توضیح می‌دهد.

آماده‌سازی سرورها و شبکه

هر سه سرور باید روی یک شبکه مشترک قابل دسترس به هم باشند، ترجیحا با IP خصوصی تا ترافیک همگام‌سازی روی اینترنت عمومی رد و بدل نشود. اگر پلن شما IP خصوصی ندارد، همان IP عمومی را استفاده کنید اما در فایروال فقط به سه IP مشخص اجازه ورود بدهید؛ در ادامه همین کار را انجام می‌دهیم.

نام هر سرور را در فایل /etc/hosts هر سه ماشین ثبت کنید تا با نام، نه با IP، به هم وصل شوند:

sudo tee -a /etc/hosts <<'EOF'
10.0.0.11 mongo-node1
10.0.0.12 mongo-node2
10.0.0.13 mongo-node3
EOF

این سه آدرس را با IP واقعی سرورهای خودتان جایگزین کنید. ساعت سیستم هر سه سرور هم باید هماهنگ باشد؛ اختلاف زیاد ساعت می‌تواند روی election و بررسی سلامت اعضا اثر بگذارد:

sudo timedatectl set-ntp true
timedatectl status

نصب MongoDB روی هر سه سرور

مراحل زیر را روی هر سه سرور، به‌ترتیب، تکرار کنید. نمونه برای Ubuntu 24.04 است:

curl -fsSL https://pgp.mongodb.com/server-8.0.asc | \
  sudo gpg --dearmor -o /usr/share/keyrings/mongodb-server-8.0.gpg
echo "deb [signed-by=/usr/share/keyrings/mongodb-server-8.0.gpg] https://repo.mongodb.org/apt/ubuntu noble/mongodb-org/8.0 multiverse" | \
  sudo tee /etc/apt/sources.list.d/mongodb-org-8.0.list
sudo apt update
sudo apt install -y mongodb-org

برای Rocky Linux یا AlmaLinux، مخزن معادل را طبق راهنمای رسمی MongoDB برای RHEL اضافه کنید و با dnf install mongodb-org نصب کنید. بعد از نصب روی هر سرور، سرویس را فعال کنید اما هنوز استارت نهایی را انجام ندهید؛ اول باید mongod.conf را تغییر دهیم.

پیکربندی mongod.conf

فایل /etc/mongod.conf روی هر سه سرور را ویرایش کنید. سه بخش مهم: آدرسی که mongod به آن گوش می‌دهد، مسیر داده، و نام Replica Set:

net:
  port: 27017
  bindIp: 127.0.0.1,10.0.0.11   # IP خودِ همین سرور را بگذارید

storage:
  dbPath: /var/lib/mongodb

replication:
  replSetName: rs0

replSetName باید روی هر سه سرور دقیقا یک مقدار یکسان باشد؛ هر مقداری که انتخاب کنید کار می‌کند، فقط باید یکسان باشد. bindIp را روی هر سرور با IP خودش پر کنید، نه IP سرورهای دیگر. بعد از ذخیره فایل، سرویس را روی هر سه سرور استارت کنید:

sudo systemctl enable --now mongod
systemctl status mongod

فایروال: فقط سه سرور به هم دسترسی داشته باشند

پورت 27017 نباید برای کل اینترنت باز باشد. با ufw روی هر سرور فقط به دو سرور دیگر اجازه دسترسی بدهید:

sudo ufw allow from 10.0.0.11 to any port 27017
sudo ufw allow from 10.0.0.12 to any port 27017
sudo ufw allow from 10.0.0.13 to any port 27017
sudo ufw enable

روی هر سرور، قانون مربوط به خودش هم ضرری ندارد ولی لازم نیست؛ مهم این است که سه قانون بالا روی هر سه ماشین با IPهای درست تکرار شود. اگر از فایروال پنل سرویس‌دهنده هم استفاده می‌کنید، همین سه IP را به‌عنوان مبدا مجاز اضافه کنید، نه بازه باز. برای قوانین پیچیده‌تر یا مهاجرت از iptables، راه‌اندازی فایروال nftables روی سرور لینوکس را ببینید.

دیاگرام فایروال محدود به سه IP بین اعضای Replica Set MongoDB

راه‌اندازی Replica Set با rs.initiate

وارد mongo shell روی mongo-node1 شوید:

mongosh

و Replica Set را با همین سرور به‌عنوان عضو اول راه‌اندازی کنید:

rs.initiate({
  _id: "rs0",
  members: [
    { _id: 0, host: "mongo-node1:27017" },
    { _id: 1, host: "mongo-node2:27017" },
    { _id: 2, host: "mongo-node3:27017" }
  ]
})

بعد از چند ثانیه، وضعیت را بررسی کنید:

rs.status()

در خروجی باید یک عضو PRIMARY و دو عضو SECONDARY ببینید. اگر عضوی در وضعیت STARTUP2 یا (not reachable) ماند، همگام‌سازی اولیه (initial sync) هنوز تمام نشده یا فایروال/DNS بین سرورها مشکل دارد؛ لاگ را با sudo journalctl -u mongod -n 50 چک کنید.

احراز هویت بین اعضا با Keyfile

تا این مرحله، هر برنامه‌ای که به شبکه داخلی دسترسی دارد می‌تواند بدون رمز به کلاستر وصل شود. برای یک محیط عملیاتی باید authorization را فعال کنید و اعضا هم باید با یک کلید مشترک به هم اعتماد کنند؛ افزایش امنیت سرور لینوکس نکات تکمیلی برای سخت‌تر کردن همین سرورها را توضیح می‌دهد. روی یکی از سرورها کلید را بسازید:

openssl rand -base64 756 > mongo-keyfile
chmod 400 mongo-keyfile

همین فایل را با scp به مسیر مشابه روی دو سرور دیگر کپی کنید، سپس روی هر سه سرور مالکیت و دسترسی را تنظیم کنید:

sudo mv mongo-keyfile /etc/mongod-keyfile
sudo chown mongodb:mongodb /etc/mongod-keyfile
sudo chmod 400 /etc/mongod-keyfile

در /etc/mongod.conf روی هر سه سرور این بخش را اضافه کنید:

security:
  keyFile: /etc/mongod-keyfile
  authorization: enabled

قبل از ریستارت سرویس‌ها، یک کاربر ادمین بسازید؛ در این لحظه authorization هنوز فعال نشده، پس دسترسی آزاد است:

use admin
db.createUser({
  user: "dbAdmin",
  pwd: "یک رمز قوی و یکتا بگذارید",
  roles: [ { role: "root", db: "admin" } ]
})

حالا mongod را روی هر سه سرور ریستارت کنید:

sudo systemctl restart mongod

از این به بعد، هر اتصال به کلاستر باید با همین کاربر و رمز وارد شود، و اعضا هم فقط با داشتن همان keyfile می‌توانند عضو کلاستر باقی بمانند:

mongosh -u dbAdmin -p --authenticationDatabase admin

تست Failover خودکار

برای تست واقعی، سرویس مربوط به عضوی که الان Primary است را متوقف کنید:

sudo systemctl stop mongod

روی یکی از دو سرور دیگر، با همان کاربر وارد mongosh شوید و rs.status() را اجرا کنید. ظرف چند ثانیه باید یکی از دو Secondary باقی‌مانده به PRIMARY تبدیل شده باشد. در همین حین، اگر برنامه‌ای با connection string شامل هر سه هاست (mongodb://mongo-node1,mongo-node2,mongo-node3/?replicaSet=rs0) و retryWrites=true وصل باشد، فقط چند ثانیه خطای موقت می‌بیند و خودش دوباره به Primary جدید وصل می‌شود.

بعد از تست، سرویس سرور متوقف‌شده را دوباره بالا بیاورید؛ این سرور به‌صورت Secondary به کلاستر برمی‌گردد و oplog را از جایی که مانده بود ادامه می‌دهد:

sudo systemctl start mongod

نگهداری بعد از راه‌اندازی

اندازه oplog پیش‌فرض معمولا کافی است، اما اگر همگام‌سازی مجدد یک عضو بعد از قطعی طولانی طول بکشد، باید اندازه آن را با replication.oplogSizeMB در mongod.conf افزایش دهید. وضعیت کلاستر را به‌طور منظم با rs.status() و rs.printSecondaryReplicationInfo() بررسی کنید تا تاخیر همگام‌سازی هر عضو را ببینید.

Replica Set جای بکاپ را نمی‌گیرد؛ یک دستور حذف اشتباه روی Primary، روی همه نسخه‌ها هم اجرا می‌شود. برای یک پشتیبان مستقل، mongodump را از یک Secondary اجرا کنید تا بار اضافه روی Primary نیفتد؛ جزئیات بازیابی و انتقال را راهنمای پشتیبان‌گیری، بازیابی و انتقال پایگاه داده MongoDB پوشش می‌دهد:

mongodump --host mongo-node2:27017 -u dbAdmin -p --authenticationDatabase admin --out /backup/$(date +%F)

اگر کلاستر روی سرور مجازی بالا رفته و بار واقعی سرویس بیشتر شد، همان سه سرور را بدون تغییر در برنامه به پلن قوی‌تر یا به سرور اختصاصی منتقل کنید؛ Replica Set همین مهاجرت را هم آسان‌تر می‌کند، چون می‌توانید یک عضو را جایگزین کنید بدون این‌که سرویس کامل خاموش شود.

برای شروع همین معماری، سه سرور مجازی آریانت کافی است؛ اگر از ابتدا به دیسک اختصاصی یا منابع تضمین‌شده نیاز دارید، سرور اختصاصی آریانت را هم بررسی کنید.

جمع‌بندی

راه‌اندازی MongoDB Replica Set سه‌عضوی چند مرحله مشخص دارد: شبکه و فایروال محدود بین سه سرور، نصب یکسان روی هر سرور، یک replSetName مشترک، rs.initiate با سه عضو، و در آخر keyfile به‌همراه authorization برای این‌که فقط اعضای واقعی کلاستر به هم وصل شوند. تست Failover را فراموش نکنید؛ تنها راه اطمینان از این‌که election کار می‌کند، خاموش کردن Primary و دیدن نتیجه با چشم خودتان است.

سؤالات متداول

آیا با دو سرور هم می‌شود Replica Set ساخت؟ می‌شود، اما در صورت قطع شبکه بین آن دو، هیچ عضوی به‌تنهایی نمی‌تواند با رای اکثریت Primary شود. برای election قابل‌اعتماد حداقل سه عضو رای‌دهنده لازم است.

چه فرقی بین Replica Set و Sharding هست؟ Replica Set همان داده را روی چند سرور کپی می‌کند تا در برابر خرابی مقاوم باشد. Sharding برای زمانی است که حجم داده یا بار روی یک سرور جا نمی‌شود و داده بین چند کلاستر تقسیم می‌شود؛ این دو معمولا با هم استفاده می‌شوند، نه به‌جای هم.

Failover چقدر طول می‌کشد و برنامه در این مدت چه می‌بیند؟ election معمولا چند ثانیه طول می‌کشد. برنامه‌ای که با درایور رسمی و retryWrites=true وصل است، در همین چند ثانیه خطای موقت connection می‌بیند و بعد خودکار به Primary جدید وصل می‌شود؛ نیازی به تغییر کد در لحظه قطعی نیست.

آیا هر سه سرور باید در یک دیتاسنتر باشند؟ در یک دیتاسنتر بودن تاخیر همگام‌سازی را کمتر می‌کند و برای شروع ساده‌تر است. پخش اعضا در چند دیتاسنتر هم ممکن است و در برابر خرابی کل یک دیتاسنتر محافظت بیشتری می‌دهد، اما تاخیر شبکه بین آن‌ها باید قبل از راه‌اندازی اندازه‌گیری شود.