במדריך הזה נסביר איך לפרוס סטאק של PostgreSQL עם זמינות גבוהה בשלושה אזורים בסביבת Google Distributed Cloud (GDC) עם פער אוויר. תלמדו איך להכין את פריטי התוכנה הנדרשים, להפעיל את המכונות הווירטואליות המיועדות ולהשתמש ב-Autobase כדי להפוך את כל תהליך ההקצאה לאוטומטי. במדריך הזה, Patroni משמש כשכבת הניהול הראשית לתזמור מחזור החיים של PostgreSQL ולטיפול ביתירות כשל אוטומטית.
ארכיטקטורה
הארכיטקטורה מורכבת מסביבה של שלוש מכונות וירטואליות שפרוסות על פני שלושה אזורי זמינות.

כל מכונה וירטואלית זהה ומריצה סטאק של שירותים באותו מיקום:
- PostgreSQL 17: המנוע המרכזי של מסד הנתונים הרלציוני.
- Patroni: מנהל הזמינות הגבוהה. הוא מטפל במחזור החיים של תהליך PostgreSQL ומבצע יתירות כשל אוטומטית. הוא חושף API בארכיטקטורת REST של HTTPS ביציאה
8008(נקודת הקצה/primary) שמשמשת את מאזן העומסים כדי לזהות את ה-leader הנוכחי. - etcd: מאגר ההגדרות המבוזר (DCS). הוא מספק את שכבת הקונצנזוס לבחירת המנהיג ומאחסן את ההגדרות של Patroni.
- PgBouncer: מאגר חיבורים שיושב לפני PostgreSQL כדי לייצב את התקורה של החיבור. הוא מספק את נקודת הכניסה המומלצת לתנועת נתונים של אפליקציות ביציאה
6432.
הסטאק כולל גם מאזן עומסים גלובלי ברמת L4 ב-GDC עם air gap, שהוא שירות מנוהל בפלטפורמה שמספק כתובת IP וירטואלית (VIP) יציבה. האפליקציות מתחברות לכתובת ה-VIP היציבה ביציאה 6432, ומאזן העומסים מנתב אותן ל-PgBouncer במכונה הווירטואלית הראשית הנוכחית. לאחר מכן, PgBouncer מעביר את הבקשה באמצעות שרת proxy למכונה המקומית של PostgreSQL. כדי לנהל את זרימת התנועה, מאזן העומסים דוגם באופן רציף את נקודות הקצה של Patroni HTTPS כבדיקת תקינות.
בדיקת התקינות במכונה הווירטואלית הראשית מחזירה את הערך HTTP 200 OK כדי לאותת שהמכונה הווירטואלית מוכנה לתעבורת נתונים, ובדיקות התקינות במכונות הווירטואליות המשוכפלות מחזירות את הערך HTTP 503 Service Unavailable כדי לאותת למאזן העומסים לעקוף אותן. אם המכונה הראשית נכשלת, נבחרת מכונה חדשה ומופע Patroni שלה מתחיל להחזיר את הערך HTTP 200 OK, מה שגורם למאזן העומסים להפנות באופן אוטומטי את התנועה ליציאת PgBouncer של המכונה הווירטואלית החדשה.
כדי להבטיח זמינות גבוהה ולמנוע אובדן נתונים, המערכת מסתמכת על קוורום (quorum). במקרה של 3 מכונות וירטואליות, המערכת דורשת שרוב של לפחות שני חברים יהיו תקינים ויתקשרו כדי לבחור מנהיג ולהישאר פעילים. הקונצנזוס הזה, שמבוסס על רוב ומנוהל על ידי etcd ו-Patroni, מאפשר למחסנית לסבול באופן אוטומטי כשל מוחלט של כל מכונה וירטואלית או אזור.
שיקולי ביצועים
כשמתכננים את הפריסה, כדאי להביא בחשבון את הגורמים הבאים כדי לשפר את הביצועים והאמינות:
- התאמת גודל החומרה: הדרישות משתנות בהתאם לעומס העבודה, אבל אפשר להשתמש בפרופילים הסטנדרטיים האלה כנקודת התחלה לכל מכונה וירטואלית:
- פיתוח/הוכחת היתכנות: 2 vCPU, 8GB RAM (מינימום לפעולה יציבה).
- סביבת ייצור קטנה: 4 vCPU, 16GB RAM. מתאים לכלים פנימיים עם בו-זמניות מתונה.
- ייצור רגיל: 8 vCPU, 32GB RAM. ההמלצה הבסיסית לאפליקציות קריטיות.
- תפוקה גבוהה: 16+ vCPU, 64GB+ RAM. לעומסי עבודה שדורשים שמירה נרחבת של נתונים במטמון בזיכרון (מאגרי נתונים זמניים משותפים של PostgreSQL).
- ביצועי האחסון: חשוב מאוד להשתמש באחסון עם ביצועים גבוהים. מומלץ מאוד להשתמש בדיסקים מסוג SSD כדי לשמור על היציבות של etcd. etcd רגיש מאוד לזמן האחזור של כתיבה בדיסק. בהנחיות הרשמיות של etcd בנושא חומרה מומלץ שזמן האחזור של fdatasync ב-WAL בדיסק p99 יהיה פחות מ-10 אלפיות השנייה.
- זמן האחזור של הרשת: זמן האחזור בין המכונות הווירטואליות משפיע ישירות על ביצועי הרפליקציה:
- קוורום etcd: זמן הלוך ושוב (RTT) ממוצע צריך להיות פחות מ-50 אלפיות השנייה (באופן אידיאלי פחות מ-10 אלפיות השנייה) כדי למנוע זמן קצוב לתפוגה של בחירות וחוסר יציבות של האשכול.
- רפליקציה סינכרונית: אם היא מוגדרת, כל טרנזקציית כתיבה צריכה להמתין לאישור של רפליקה. זמן האחזור בין אזורים ב-GDC עם air gap הוא בדרך כלל פחות מ-1ms, וזה מצוין כדי לשמור על תקורה מינימלית של כתיבה (בדרך כלל 10-30%).
- התפקיד של PgBouncer: מערכת PostgreSQL יוצרת תהליך חדש במערכת ההפעלה לכל חיבור, שצורך כ-10MB של זיכרון RAM וגורם לעלויות של החלפת הקשר ב-CPU. PgBouncer מפחית את התקורה הזו על ידי שמירה על מאגר של חיבורים קבועים, וכך מאפשר למסד הנתונים לטפל באלפי חיבורים של אפליקציות עם הרבה פחות תהליכים בקצה העורפי.
- רכיבי Sidecar: Patroni ו-etcd הם קלים אבל דורשים זמינות עקבית של המעבד (CPU). בתרחישים של עומס גבוה, ודאו שהמכונות הווירטואליות לא מנוצלות יתר על המידה ברמת ה-hypervisor, כדי למנוע "גניבה" של מחזורי מעבד (CPU) שנדרשים לאותות פעימות הלב ולתחזוקת ה-leader.
- כוונון הליבה: מערכת האוטומציה של Autobase מבצעת באופן אוטומטי אופטימיזציות שמועילות ל-PostgreSQL, כמו הגדרת פרמטרים של sysctl (למשל
vm.swappiness,net.core.somaxconn) והשבתת Transparent Huge Pages (THP). השינויים האלה מפחיתים את התקורה של ניהול הזיכרון ומשפרים את תפוקת הרשת במכונות של מסדי נתונים עם תנועה רבה.
לפני שמתחילים
לפני שמתחילים את הפריסה, צריך לוודא שהסביבה עומדת בדרישות הבאות.
בדיקת הדרישות של המכונה הווירטואלית
במדריך הזה, צריך ליצור שלוש מכונות וירטואליות בפרויקט GDC עם air gap. צריך להביא בחשבון את הנקודות והדרישות הבאות לגבי המכונות הווירטואליות:
- הפצה לאזורים: כדי שהפריסה הזו תהיה עמידה באמת בפני כשל באזור, צריך להפיץ את המכונות הווירטואליות בשלושה אזורי זמינות שונים. עם זאת, הפריסה נשארת זהה אם המכונות הווירטואליות נמצאות בשני אזורים או אפילו באזור אחד. הדבר החשוב ביותר הוא שכל המכונות הווירטואליות יוכלו לתקשר זו עם זו ברשת באמצעות כתובות ה-IP הפנימיות שלהן.
- מערכת הפעלה: במדריך הזה אנחנו מניחים שאתם משתמשים בתמונות של Ubuntu 22.04. אם אתם משתמשים בהפצה אחרת, יכול להיות שהשלבים הבאים במדריך יהיו שונים.
- משאבים: במדריך הזה צריך להקצות לפחות 2 מעבדים (CPU) וזיכרון בנפח 8GB לכל מכונה וירטואלית. בסביבת הייצור, צריך להקצות משאבים שמתאימים לעומסי העבודה הספציפיים (ראו שיקולי ביצועים).
- כתובות IP ברשת: חשוב לשים לב לכתובות ה-IP הפנימיות והחיצוניות של כל מכונה וירטואלית. במדריך הזה, אתם משתמשים בכתובות IP חיצוניות לבקרת Ansible כי אתם מריצים פקודות מתחנת עבודה חיצונית. כתובות IP פנימיות משמשות לתקשורת בין שירותים ולקישור. אם הקציתם מכונה וירטואלית של bootstrapper בתוך הרשת, תצטרכו רק את כתובות ה-IP הפנימיות.
- גישה: נדרשת גישת sudo ללא סיסמה למשתמש הפריסה, כי האוטומציה של Ansible צריכה לבצע משימות ניהוליות (התקנת חבילות, שינוי הגדרות המערכת) בלי להיחסם על ידי בקשות להזנת סיסמה.
- SSH: צריך להפעיל אימות מבוסס-מפתח כדי לאפשר ל-Ansible להתחבר למכונות הווירטואליות ללא אינטראקציה ובצורה מאובטחת.
הכנת התוכנה של תחנת העבודה המקומית
כדי לנהל את הפריסה ולהכין את פריטי המידע שנוצרו בתהליך הפיתוח (Artifact) עם air gap, תצטרכו להתקין בתחנת העבודה המקומית קבוצה של כלי אוטומציה וקונטיינריזציה.
- Ansible 2.17.0+: מנוע האוטומציה שמריץ את תרחישי הפריסה והתפקידים.
- Docker: משמש למשיכה ולאריזה של יחסי תלות של מערכת ההפעלה בסביבה שזהה למכונות הווירטואליות המטורגטות (Ubuntu 22.04).
- לקוח PostgreSQL (
psql): נדרש כדי להריץ את שאילתות הבדיקה ולאמת את שכפול הנתונים מתחנת העבודה המקומית. המאגר של Autobase:
- משכפלים את המאגר כדי לגשת לתפקידים ולפלייבוקים של האוטומציה: https://github.com/vitabaks/autobase
בודקים גרסה ספציפית (במדריך הזה משתמשים בגרסה 2.5.2):
git checkout 2.5.2אם אתם משתמשים בהפצה אחרת, יכול להיות שהשלבים הבאים במדריך יהיו שונים.
כדי להריץ את הפלייבוקים במדריך הזה, צריך להתקין את קוד המקור המקומי של
autobaseכאוסף Ansible, כדי שאפשר יהיה לפתור את הקידומות של התפקידים:cd autobase/automation ansible-galaxy collection install . --force
יצירת משתני סביבה
במדריך הזה תשתמשו במשתני הסביבה הבאים כדי לפשט את הפקודות. המשתנים האלה מאחסנים פרמטרים קריטיים כמו מזהה הפרויקט, אזורי הזמינות של המכונות הווירטואליות, שמות המארחים שלהן והתוויות שבהן משתמש מאזן העומסים כדי לזהות את האשכול. מגדירים אותם בסשן הנוכחי של המעטפת עם הערכים בפועל של הסביבה (מוודאים שהאזורים מופרדים ברווחים).
שימו לב שאפשר לתת למכונות הווירטואליות כל שם שרוצים, אבל במדריך הזה נשתמש בשמות postgres-vm-1, postgres-vm-2 ו-postgres-vm-3 כשמות שרירותיים לדוגמה לצומתי האשכול:
export PROJECT_ID="your-project-id"
export ZONES="zone1 zone2 zone3"
export VM1_NAME="postgres-vm-1"
export VM2_NAME="postgres-vm-2"
export VM3_NAME="postgres-vm-3"
export VM_LABEL="app=my-postgres-cluster"
export CLUSTER_NAME="my-postgres-cluster"
הגדרת Ansible
מגדירים את סביבת ה-VM בקובץ inventory.ini באמצעות התבנית הבאה:
[master]
postgres-vm-1 ansible_host=XX.XX.XX.XX hostname=postgres-vm-1 bind_address=XX.XX.XX.XX
[replica]
postgres-vm-2 ansible_host=XX.XX.XX.XX hostname=postgres-vm-2 bind_address=XX.XX.XX.XX
postgres-vm-3 ansible_host=XX.XX.XX.XX hostname=postgres-vm-3 bind_address=XX.XX.XX.XX
[postgres_cluster:children]
master
replica
[etcd_cluster]
postgres-vm-1
postgres-vm-2
postgres-vm-3
[all:vars]
ansible_user=...
ansible_ssh_private_key_file=~/.ssh/...
postgresql_version=17
with_haproxy_load_balancing=false
patroni_superuser_password=...
etcd_package_repo="file:///tmp/packages/etcd-v3.5.25-linux-amd64.tar.gz"
installation_method="packages"
install_postgresql_repo=false
install_timescale_repo=false
install_citus_repo=false
apt_repository=[]
yum_repository=[]
install_system_packages=false
patroni_installation_method=deb
הסבר על ההגדרות:
-
[master]ו-[replica]: מגדירים את המכונות הווירטואליות של מסד הנתונים הראשי והמשני. חשוב להשתמש בשמות האמיתיים של המכונות הווירטואליות שהוגדרו במשתני הסביבהVM1_NAME,VM2_NAMEו-VM3_NAME. -
[postgres_cluster:children]: קבוצה שמאגדת את צומתי המאסטר והרפליקה, ומאפשרת ל-Ansible לטרגט את כל אשכול מסד הנתונים באמצעות פקודה אחת. -
[etcd_cluster]: מגדיר את הצמתים שישתתפו באשכול הקונצנזוס של etcd. הוא כולל את כל שלושת צומתי מסד הנתונים כדי להבטיח זמינות גבוהה. -
ansible_host: (לכל מכונה וירטואלית) כתובת ה-IP החיצונית של תעבורת הנתונים הנכנסת של המכונה הווירטואלית, שמשמשת את Ansible כדי להתחבר למכונה הווירטואלית הזו. מחליפים אתXX.XX.XX.XXבכתובת ה-IP החיצונית בפועל. -
bind_address: (לכל מכונה וירטואלית) כתובת ה-IP הפנימית של המכונה הווירטואלית. מחליפים אתXX.XX.XX.XXבכתובת ה-IP הפנימית בפועל. -
ansible_user: המשתמש המרוחק שבו Ansible משתמש כדי להתחבר למכונות הווירטואליות המטורגטות באמצעות SSH. מחליפים את...בשם המשתמש בפועל. -
ansible_ssh_private_key_file: הנתיב המקומי למפתח ה-SSH הפרטי שמשמש לאימות במכונות הווירטואליות המיועדות להעברה. מחליפים את~/.ssh/...בנתיב בפועל. -
patroni_superuser_password: הסיסמה של המשתמשpostgres. חשוב להשתמש בסיסמה חזקה ומאובטחת. -
with_haproxy_load_balancing=false: משבית את HAProxy המקומי כי אתם משתמשים במאזן העומסים L4 המקורי של הפלטפורמה. -
etcd_package_repo: מצביע על הנתיב המקומי של הקובץ הבינארי של etcd בתוך ספריית האתחול של המכונה הווירטואלית. -
installation_method="packages": מורה לאוטומציה להתקין רכיבים עם חבילות של מערכת ההפעלה, במקום להדר מהמקור או להשתמש ב-Python pip. -
install_..._repo=falseו-_repository=[]: ההגדרות האלה מונעות מ-Ansible לנסות להתחבר לאינטרנט כדי להוסיף מאגרים חיצוניים או לעדכן רשימות של חבילות. -
install_system_packages=false: מונע מהאוטומציה לנסות להוריד ולהתקין חבילות שכבר הקציתם במהלך שלב האתחול או ה-bootstrap. -
patroni_installation_method=deb: מציין לתפקיד להשתמש בחבילת.debשהתקנתם.
אתחול המכונות הווירטואליות
מחסנית מסד הנתונים דורשת כמה חבילות וספריות של מערכת ההפעלה, שיכול להיות שלא נכללות בקובץ האימג' הבסיסי של Ubuntu. מכיוון שהמכונות הווירטואליות נמצאות בסביבה עם air gap ללא גישה לאינטרנט, הן לא יכולות להוריד את יחסי התלות האלה בעצמן.
כדי לפתור את הבעיה:
משתמשים בקונטיינר של Docker בתחנת העבודה המקומית כדי להוריד את כל הקבצים הנדרשים. הפקודה הבאה משתמשת ב-
apt-rdependsכדי לזהות באופן רקורסיבי כל ספרייה משותפת וכל תלות שנדרשות לאפליקציות היעד. הוא מגדיר את מאגר PostgreSQL הרשמי בתוך הקונטיינר כדי לאחזר פריטי מידע שנוצרו בתהליך הפיתוח (artifacts) של גרסה 17, ואז עובר על רשימת יחסי התלות כדי להוריד קובצי.debנפרדים תוך סינון של ספריות מערכת ליבה (כמוlibc6אוhostname) כדי למנוע התנגשויות בין גרסאות במכונות הווירטואליות של היעד. לבסוף, הוא מאחזר את הקובץ הבינארי העצמאי של etcd ישירות מ-GitHub.קודם כל, יוצרים ספרייה שתכיל את החבילות:
mkdir -p ./packagesאחר כך מריצים את הפקודה של Docker כדי להוריד את כל החבילות הנדרשות ואת הקובץ הבינארי של etcd:
docker run --rm --platform linux/amd64 -v "$(pwd)/packages:/packages" \ ubuntu:22.04 bash -c " set -e apt-get update apt-get install -y ca-certificates curl gnupg apt-rdepends # Add PostgreSQL Repository curl -fsSL https://www.postgresql.org/media/keys/ACCC4CF8.asc | \ gpg --dearmor -o /etc/apt/trusted.gpg.d/postgresql.gpg echo 'deb http://apt.postgresql.org/pub/repos/apt jammy-pgdg main' > \ /etc/apt/sources.list.d/pgdg.list apt-get update # Define Application Targets + Explicit dependencies needed for air-gap TARGETS='unzip tar pgbouncer patroni netdata postgresql-17 \ postgresql-client-17 postgresql-contrib-17 \ postgresql-server-dev-17 postgresql-17-dbgsym \ python3-psycopg2 python3-click python3-yaml python3-prettytable \ python3-urllib3 python3-tz python3-pip python3-setuptools \ python3-cryptography moreutils vim jq acl zstd libjq1 \ libpython3.10-stdlib libexpat1-dev zlib1g-dev libipc-run-perl \ libtime-duration-perl libjson-perl libpython3-dev \ libjs-sphinxdoc python3-wheel' # Resolve all recursive dependencies ALL_DEPS=\$(apt-cache depends --recurse --no-recommends --no-suggests \ --no-conflicts --no-breaks --no-replaces --no-enhances \$TARGETS | \ grep '^\w' | sort -u) cd /packages for pkg in \$ALL_DEPS; do if apt-cache show \"\$pkg\" > /dev/null 2>&1; then # Filter system core to avoid VM conflicts/breaks # We exclude core OS libraries (libc, systemd, etc.) because these # often cause version conflicts if the VM's patch level differs # from the online container. FILTER='base-files|debianutils|coreutils|findutils|diffutils|sed' FILTER+='|grep|gzip|hostname|ncurses|perl-base|libc6|binutils' FILTER+='|linux-libc|libc-bin|libc-dev-bin|systemd|dpkg|init' if [[ ! \"\$pkg\" =~ \$FILTER ]]; then apt-get download \"\$pkg\" || echo \"Failed \$pkg\" fi fi done # Download etcd binary if [ ! -f etcd-v3.5.25-linux-amd64.tar.gz ]; then curl -L https://github.com/etcd-io/etcd/releases/download/v3.5.25/\ etcd-v3.5.25-linux-amd64.tar.gz -o etcd-v3.5.25-linux-amd64.tar.gz fi "משתמשים ב-Ansible כדי להעלות את הארכיון לכל שלוש המכונות הווירטואליות בו-זמנית:
ansible all -i inventory.ini -m copy -a "src=packages.tar.gz dest=/tmp/" -bמוחקים את כל נתוני החבילות הקיימים במכונות הווירטואליות ומחלצים את קובץ ה-tar החדש:
ansible all -i inventory.ini -m shell -a "rm -rf /tmp/packages && \ mkdir -p /tmp/packages && tar -xzf /tmp/packages.tar.gz -C /tmp/packages" -bמבצעים התקנה לא אינטראקטיבית של כל חבילות
.debשהורדו. כדי למנוע בעיות עם יחסי תלות מוקדמים ספציפיים בסביבה עם air gap, השתמשו בדגל--force-dependsואחריו ב-apt-get install -fyכדי לפתור את עץ יחסי התלות באופן מקומי:ansible all -i inventory.ini -m shell -a "DEBIAN_FRONTEND=noninteractive \ NEEDRESTART_MODE=a dpkg -i --force-depends /tmp/packages/*.deb" -b ansible all -i inventory.ini -m shell -a "DEBIAN_FRONTEND=noninteractive \ NEEDRESTART_MODE=a apt-get install -fy" -bעצרו מיד את כל השירותים כדי למנוע מהם להתחיל במצבי ברירת מחדל לא מוגדרים לפני שהאוטומציה מוכנה:
ansible all -i inventory.ini -m shell -a \ "systemctl stop patroni etcd pgbouncer postgresql || true" -bבשלב האחרון, מסירים את אשכולות ברירת המחדל של PostgreSQL ואת כל נתוני ה-etcd הקיימים כדי לאפשר אתחול נקי:
ansible all -i inventory.ini -m shell -a \ "pg_dropcluster 17 main --stop || true" -b ansible all -i inventory.ini -m shell -a \ "rm -rf /var/lib/postgresql/17/main/* /var/lib/etcd/default.etcd/*" -b
הקצאת תשתית למסד נתונים
אחרי שהמכונות הווירטואליות אותחלו והמלאי הוגדר, אתם יכולים להשתמש בספרי ההדרכה של Autobase לאוטומציה עם Ansible כדי לפרוס את מחסנית PostgreSQL בזמינות גבוהה.
קודם כל, מריצים את בדיקות טרום-ההרצה כדי לוודא שהסביבה מוכנה:
ansible-playbook vitabaks.autobase.deploy_pgcluster -i inventory.ini \
--tags pre_checks
אם הבדיקות עוברות, ממשיכים בפריסה המלאה:
ansible-playbook vitabaks.autobase.deploy_pgcluster -i inventory.ini
הפלט הצפוי: הפלייבוק אמור להסתיים בהצלחה עם "PLAY RECAP" ולהראות שכל המכונות הווירטואליות של היעד עודכנו:
PLAY RECAP ********************************************************************
localhost : ok=1 changed=0 unreachable=0 failed=0 skipped=254 rescued=0 ignored=0
postgres-vm-1 : ok=160 changed=53 unreachable=0 failed=0 skipped=514 rescued=0 ignored=2
postgres-vm-2 : ok=116 changed=40 unreachable=0 failed=0 skipped=505 rescued=0 ignored=2
postgres-vm-3 : ok=116 changed=40 unreachable=0 failed=0 skipped=505 rescued=0 ignored=2
אימות הפריסה
אחרי שהפריסה מסתיימת, כדאי לבצע כמה בדיקות כדי לוודא שכל הרכיבים פועלים כמו שצריך.
בדיקת הסטטוס של הזמינות הגבוהה (HA)
בודקים את הסטטוס של מנהל הזמינות הגבוהה כדי לראות את התפקידים שהוקצו לכל מכונה וירטואלית:
ansible master -i inventory.ini -m shell -a "patronictl list" -b
פלט לדוגמה:
+ Cluster: postgres-cluster (7607933704953386478) ---+----+-------------+-----+------------+-----+
| Member | Host | Role | State | TL | Receive LSN | Lag | Replay LSN | Lag |
+---------------+--------------+---------+-----------+----+-------------+-----+------------+-----+
| postgres-vm-1 | 10.253.1.254 | Leader | running | 1 | | | | |
| postgres-vm-2 | 10.253.1.253 | Replica | streaming | 1 | 0/6000000 | 0 | 0/6000000 | 0 |
| postgres-vm-3 | 10.253.1.252 | Replica | streaming | 1 | 0/6000000 | 0 | 0/6000000 | 0 |
+---------------+--------------+---------+-----------+----+-------------+-----+------------+-----+
אימות נקודות קצה (endpoint) של בדיקת תקינות
בודקים ש-Patroni מזהה בצורה נכונה את ה-leader ואת הרפליקות באמצעות ה-API בארכיטקטורת REST.
בדיקת התקינות של ה-VM הראשי אמורה להחזיר את הערך 200 OK, ובדיקות התקינות של ה-VM המשוכפלים אמורות להחזיר את הערך 503 Service Unavailable:
ansible ${VM1_NAME} -i inventory.ini -m shell -a \
"curl -ks -o /dev/null -w '%{http_code}' https://localhost:8008/primary" -b
ansible ${VM2_NAME} -i inventory.ini -m shell -a \
"curl -ks -o /dev/null -w '%{http_code}' https://localhost:8008/primary" -b
ansible ${VM3_NAME} -i inventory.ini -m shell -a \
"curl -ks -o /dev/null -w '%{http_code}' https://localhost:8008/primary" -b
אימות התקינות של מכונות וירטואליות ספציפיות
בודקים את המוכנות של כל מכונות PostgreSQL:
ansible postgres_cluster -i inventory.ini -m shell -a "pg_isready -p 5432" -b
פלט לדוגמה:
postgres-vm-1 | CHANGED | rc=0 >>
/var/run/postgresql:5432 - accepting connections
postgres-vm-2 | CHANGED | rc=0 >>
/var/run/postgresql:5432 - accepting connections
postgres-vm-3 | CHANGED | rc=0 >>
/var/run/postgresql:5432 - accepting connections
אימות התקינות של etcd
בודקים את התקינות של שכבת הקונצנזוס בכל המכונות הווירטואליות, באמצעות localhost כנקודת הקצה:
ansible all -i inventory.ini -m shell -a "ETCDCTL_API=3 etcdctl \
--endpoints=https://localhost:2379 \
--cacert=/etc/etcd/tls/ca.crt \
--cert=/etc/etcd/tls/server.crt \
--key=/etc/etcd/tls/server.key \
endpoint health" -b
פלט לדוגמה:
postgres-vm-1 | CHANGED | rc=0 >>
https://localhost:2379 is healthy: successfully committed proposal: \
took = 28.78311ms
postgres-vm-2 | CHANGED | rc=0 >>
https://localhost:2379 is healthy: successfully committed proposal: \
took = 33.081265ms
postgres-vm-3 | CHANGED | rc=0 >>
https://localhost:2379 is healthy: successfully committed proposal: \
took = 24.414291ms
הגדרת מאזן העומסים הגלובלי
כדי לספק כתובת IP וירטואלית (VIP) יציבה למקבץ מסדי הנתונים, מגדירים את מאזן העומסים הגלובלי ברמת L4 שמותאם לפלטפורמה באמצעות ה-CLI של gdcloud.
דרישות מוקדמות:
- מוודאים שיש לכם את התפקיד
load-balancer-adminבפרויקט. מוסיפים תווית למכונות הווירטואליות כדי שמאזן העומסים יוכל לטרגט בצורה נכונה את המכונות שהוא צריך להציג (מחליפים את ערכי הפרמטר
kubeconfigבקובצי ה-Management APIkubeconfigהמתאימים לכל אזור):kubectl --kubeconfig=ZONE_A_MANAGEMENT_API label VirtualMachine \ -n ${PROJECT_ID} \ ${VM1_NAME} \ ${VM_LABEL} kubectl --kubeconfig=ZONE_B_MANAGEMENT_API label VirtualMachine \ -n ${PROJECT_ID} \ ${VM2_NAME} \ ${VM_LABEL} kubectl --kubeconfig=ZONE_C_MANAGEMENT_API label VirtualMachine \ -n ${PROJECT_ID} \ ${VM3_NAME} \ ${VM_LABEL}
- מוודאים שיש לכם את התפקיד
מגדירים את רמת הגישה לאיזון העומסים. מגדירים את
EXTERNALאם צריך להתחבר מחוץ לרשת של הפרויקט, או אתINTERNALאם נדרשת גישה רק מתוך ה-VPC. במדריך הזה נשתמש בהגדרה חיצונית:export LB_SCHEME=EXTERNALיוצרים בדיקת תקינות. מאזן העומסים משתמש ב-API בארכיטקטורת REST של Patroni כדי לזהות את ה-leader:
gdcloud compute health-checks create https ${CLUSTER_NAME}-hc \ --project=${PROJECT_ID} \ --port=8008 \ --request-path="/primary" \ --check-interval=10 \ --timeout=5 \ --healthy-threshold=2 \ --unhealthy-threshold=3 \ --globalיוצרים קצה עורפי נפרד לכל אזור שבו נמצאות המכונות הווירטואליות:
for zone in $(echo $ZONES); do gdcloud compute backends create ${CLUSTER_NAME}-backend-${zone} \ --project=${PROJECT_ID} \ --zone=${zone} \ --labels="${VM_LABEL}" doneיוצרים שירות לקצה עורפי גלובלי:
gdcloud compute backend-services create ${CLUSTER_NAME}-bes \ --project=${PROJECT_ID} \ --health-check="${CLUSTER_NAME}-hc" \ --globalמוסיפים את שירותי הקצה העורפי האזוריים לשירות הגלובלי:
for zone in $(echo $ZONES); do gdcloud compute backend-services add-backend ${CLUSTER_NAME}-bes \ --project=${PROJECT_ID} \ --backend=${CLUSTER_NAME}-backend-${zone} \ --backend-zone=${zone} \ --global doneיוצרים כלל העברה גלובלי (VIP). הכלל הזה חושף את מסד הנתונים ביציאה
6432:gdcloud compute forwarding-rules create ${CLUSTER_NAME}-fr \ --project=${PROJECT_ID} \ --load-balancing-scheme=${LB_SCHEME} \ --backend-service=${CLUSTER_NAME}-bes \ --ip-protocol-port="TCP:6432" \ --globalמאחזרים את כתובת ה-VIP:
LB_IP=$(gdcloud compute forwarding-rules describe ${CLUSTER_NAME}-fr \ --project=${PROJECT_ID} \ --load-balancing-scheme=${LB_SCHEME} \ --global \ --format=json \ | jq -r '.metadata.annotations["networking.gke.io/forwardingRuleCIDR"]' \ | cut -d '/' -f 1) echo "The load balancer IP is: ${LB_IP}"יוצרים
ProjectNetworkPolicy(PNP) כדי לאפשר תעבורת נתונים נכנסת (ingress) ליציאת PgBouncer (מחליפים את ערך הפרמטרkubeconfigבקובץ ה-API הגלובליkubeconfigשל הסביבה).kubectl --kubeconfig=GLOBAL_API_KUBECONFIG apply -f - <<EOF apiVersion: networking.global.gdc.goog/v1 kind: ProjectNetworkPolicy metadata: name: allow-pgbouncer namespace: ${PROJECT_ID} spec: ingress: - ports: - port: 6432 protocol: TCP policyType: Ingress subject: subjectType: UserWorkload EOF
אימות שכפול הנתונים
כדי לוודא שהמחסנית של הזמינות הגבוהה פועלת כמו שצריך, אתם יכולים ליצור נתונים לדוגמה ב-leader ולוודא שהם קיימים ברפליקות.
הוספת נתונים לדוגמה
אם לא צוינה סיסמה ב-inventory.ini, האוטומציה יוצרת סיסמה אקראית למשתמש postgres במהלך הפריסה הראשונה. אפשר לאחזר אותו מכל מכונה וירטואלית:
export PG_PASSWORD=$(ansible master -i inventory.ini -m shell -a \
"grep -A10 'authentication:' /etc/patroni/patroni.yml | \
grep -A3 'superuser' | grep 'password:' | awk '{ print \$2 }'" -b | \
tail -n 1)
echo $PG_PASSWORD
מתחברים ל-VIP של מאזן העומסים ביציאת PgBouncer (6432) ויוצרים טבלה לדוגמה:
PGPASSWORD="${PG_PASSWORD}" psql -h ${LB_IP} -p 6432 -U postgres -c "
CREATE TABLE employees (first_name TEXT, last_name TEXT);
INSERT INTO employees (first_name, last_name) VALUES ('John', 'Doe');
"
הפלט הצפוי:
INSERT 0 1
אימות מצב הרפליקציה
מריצים שאילתת SELECT בכל המכונות הווירטואליות כדי לוודא שהנתונים שוכפלו מהמכונה הראשית לכל הרפליקות:
ansible postgres_cluster -i inventory.ini -m shell -a "psql -U postgres -c \
'SELECT * FROM employees;'" -b
פלט לדוגמה:
postgres-vm-1 | CHANGED | rc=0 >>
first_name | last_name
------------+-----------
John | Doe
(1 row)
postgres-vm-2 | CHANGED | rc=0 >>
first_name | last_name
------------+-----------
John | Doe
(1 row)
postgres-vm-3 | CHANGED | rc=0 >>
first_name | last_name
------------+-----------
John | Doe
(1 row)
בדיקת מעבר ידני
מעבר ידני מאפשר להעביר בצורה חלקה את תפקיד ה-leader למכונה וירטואלית מועמדת ספציפית. בדרך כלל זה קורה בגלל תחזוקה מתוכננת, שדרוגי תוכנה או כדי לאזן את ניצול המשאבים בין אזורים.
זיהוי המנהיג הנוכחי
מאמתים את התפקיד והסטטוס הנוכחיים של המכונות הווירטואליות:
ansible master -i inventory.ini -m shell -a "patronictl list" -b
פלט לדוגמה:
+ Cluster: postgres-cluster (7607933704953386478) ---+----+-------------+-----+------------+-----+
| Member | Host | Role | State | TL | Receive LSN | Lag | Replay LSN | Lag |
+---------------+--------------+---------+-----------+----+-------------+-----+------------+-----+
| postgres-vm-1 | 10.253.1.254 | Leader | running | 1 | | | | |
| postgres-vm-2 | 10.253.1.253 | Replica | streaming | 1 | 0/6000000 | 0 | 0/6000000 | 0 |
| postgres-vm-3 | 10.253.1.252 | Replica | streaming | 1 | 0/6000000 | 0 | 0/6000000 | 0 |
+---------------+--------------+---------+-----------+----+-------------+-----+------------+-----+
ביצוע המעבר
מפעילים מעבר מהמכונה המובילה הנוכחית למכונה וירטואלית אחרת (במקרה הזה, מ-postgres-vm-1 ל-postgres-vm-2 בהתאמה). הפקודה משתמשת ב---force כדי לדלג על בקשות אישור ידניות:
ansible master -i inventory.ini -m shell -a "patronictl switchover \
--leader ${VM1_NAME} --candidate ${VM2_NAME} --force" -b
פלט לדוגמה:
Successfully switched over to "postgres-vm-2"
+ Cluster: postgres-cluster (7607933704953386478) -+----+-------------+-----+------------+-----+
| Member | Host | Role | State | TL | Receive LSN | Lag | Replay LSN | Lag |
+---------------+--------------+---------+---------+----+-------------+-----+------------+-----+
| postgres-vm-1 | 10.253.1.254 | Replica | stopped | | unknown | | unknown | |
| postgres-vm-2 | 10.253.1.253 | Leader | running | 1 | | | | |
| postgres-vm-3 | 10.253.1.252 | Replica | running | 1 | 0/70000A0 | 0 | 0/70000A0 | 0 |
+---------------+--------------+---------+---------+----+-------------+-----+------------+-----+
אימות של שינוי בבדיקת התקינות
אחרי המעבר, מוודאים שסטטוס בדיקת התקינות עבר ל-leader החדש:
ansible ${VM1_NAME} -i inventory.ini -m shell -a \
"curl -ks -o /dev/null -w '%{http_code}' https://localhost:8008/primary" -b
ansible ${VM2_NAME} -i inventory.ini -m shell -a \
"curl -ks -o /dev/null -w '%{http_code}' https://localhost:8008/primary" -b
ה-leader הישן (postgres-vm-1) צריך להחזיר את הערך 503, וה-leader החדש (postgres-vm-2) צריך להחזיר את הערך 200.
בדיקת יתירות כשל אוטומטית
בניגוד למעבר ידני, מעבר אוטומטי לגיבוי בעת כשל מתרחש כשהמכונה הווירטואלית הראשית לא זמינה. הבדיקה הזו מוודאת ש-Patroni בוחר מנהיג חדש, ושהמאזן העומסים מפנה את התנועה ללא התערבות ידנית. נניח ש-postgres-vm-2 הוא ה-leader הנוכחי אחרי המעבר הידני שבוצע קודם.
זיהוי המנהיג הנוכחי
מאמתים את התפקיד והסטטוס הנוכחיים של המכונות הווירטואליות:
ansible master -i inventory.ini -m shell -a "patronictl list" -b
פלט לדוגמה:
+ Cluster: postgres-cluster (7607933704953386478) ---+----+-------------+-----+------------+-----+
| Member | Host | Role | State | TL | Receive LSN | Lag | Replay LSN | Lag |
+---------------+--------------+---------+-----------+----+-------------+-----+------------+-----+
| postgres-vm-1 | 10.253.1.254 | Replica | streaming | 2 | 0/8000000 | 0 | 0/8000000 | 0 |
| postgres-vm-2 | 10.253.1.253 | Leader | running | 2 | | | | |
| postgres-vm-3 | 10.253.1.252 | Replica | streaming | 2 | 0/8000000 | 0 | 0/8000000 | 0 |
+---------------+--------------+---------+-----------+----+-------------+-----+------------+-----+
הדמיה של כשל ב-VM
עוצרים את השירות patroni במכונה הווירטואלית הראשית כדי לדמות קריסה או כשל חמור:
ansible master -i inventory.ini -m shell -a "systemctl stop patroni" -b
צפייה בבחירות החדשות
ממתינים 10-20 שניות ובודקים את הסטטוס ממכונה וירטואלית אחרת כדי לראות את הקידום של מנהיג חדש:
ansible replica -i inventory.ini -m shell -a "patronictl list" -b
פלט לדוגמה:
postgres-vm-3 | CHANGED | rc=0 >>
+ Cluster: postgres-cluster (7607933704953386478) ---+----+-------------+-----+------------+-----+
| Member | Host | Role | State | TL | Receive LSN | Lag | Replay LSN | Lag |
+---------------+--------------+---------+-----------+----+-------------+-----+------------+-----+
| postgres-vm-1 | 10.253.1.254 | Leader | running | 3 | | | | |
| postgres-vm-2 | 10.253.1.253 | Replica | stopped | | unknown | | unknown | |
| postgres-vm-3 | 10.253.1.252 | Replica | streaming | 3 | 0/90003F8 | 0 | 0/90003F8 | 0 |
+---------------+--------------+---------+-----------+----+-------------+-----+------------+-----+
אחת מהמכונות הווירטואליות האחרות (postgres-vm-1 או postgres-vm-3) אמורה להפוך ל-leader, וה-leader הישנה (postgres-vm-2) אמורה להיות מסומנת כעצורה.
אימות של שינוי בדיקת התקינות
מוודאים שבדיקות התקינות של מאזן העומסים מזהות עכשיו בצורה נכונה את ה-leader החדש שנבחר:
ansible ${VM1_NAME} -i inventory.ini -m shell -a \
"curl -ks -o /dev/null -w '%{http_code}' https://localhost:8008/primary" -b
המוביל החדש צריך להחזיר את 200 OK.
שחזור ה-VM שנכשל
מפעילים מחדש את השירות patroni במכונה הווירטואלית המקורית כדי לאפשר לה להצטרף מחדש למקבץ כרפליקה ולהשלים את הנתונים החסרים:
ansible ${VM2_NAME} -i inventory.ini -m shell -a \
"systemctl start patroni" -b