رفتن به محتوا
زیرساخت فناوری اطلاعات

پایان پشتیبانی PostgreSQL 14 در ۱۲ نوامبر ۲۰۲۶: ارتقای Postgres در Docker

PostgreSQL 14 در ۱۲ نوامبر ۲۰۲۶ آخرین نسخه‌اش را می‌گیرد و بعد از آن هیچ وصلهٔ امنیتی ندارد. چطور همهٔ کانتینرهای Postgres 14 را پیدا کنید، بین dump/restore و pg_upgrade انتخاب کنید و از سه خطایی که در ارتقای یک کلاستر آزمایشی به PostgreSQL 18 خوردیم دور بمانید.

H
Hamze Zare Nasiri
۱۰ آبان ۱۴۰۵
پایان پشتیبانی PostgreSQL 14 در ۱۲ نوامبر ۲۰۲۶: ارتقای Postgres در Docker

PostgreSQL 14 آخرین به‌روزرسانی‌اش را در ۱۲ نوامبر ۲۰۲۶ می‌گیرد. سیاست نسخه‌بندی PostgreSQL همین تاریخ را آخرین انتشار نسخهٔ ۱۴ اعلام کرده و قاعدهٔ پشتش ساده است: هر نسخهٔ اصلی پنج سال پشتیبانی می‌شود، بعد یک نسخهٔ فرعی نهایی می‌گیرد و از پشتیبانی خارج می‌شود. هر باگ امنیتی که بعد از آن روز در ۱۴ پیدا شود، بی‌وصله می‌ماند.

تقویم اطرافش هم فشرده است. اطلاعیهٔ PostgreSQL 19 RC 1 در ۱۵ اکتبر می‌گوید انتشار به‌روزرسانی ۱۲ نوامبر «تعداد زیادی اصلاح» دارد و انتشار عمومی PostgreSQL 19 را ۱۹ نوامبر ۲۰۲۶ اعلام می‌کند؛ یعنی یک هفته بعد از خروج ۱۴ از پشتیبانی. اگر هنوز روی ۱۴ هستید، باید همین حالا مقصد را انتخاب کنید، پیش از آنکه ۱۹ سابقه‌ای داشته باشد.

دیتابیس‌های مدیریت‌شده کمی فرصت بیشتری دارند. تقویم انتشار Amazon RDS for PostgreSQL نسخهٔ ۱۴ را تا ۲۸ فوریهٔ ۲۰۲۷ در پشتیبانی استاندارد نگه می‌دارد و پشتیبانی تمدیدی پولی از ۱ مارس ۲۰۲۷ شروع می‌شود. کانتینری که خودتان میزبانی می‌کنید هیچ مهلتی ندارد. ایمیج postgres:14 که در ۲۰۲۲ pull کرده‌اید، دقیقاً همان کدی را اجرا می‌کند که با آن آمده بود.

چرا این‌همه استک هنوز روی ۱۴ مانده‌اند

Postgres خطی از فایل compose است که بیشتر از همه از دست زدن به آن می‌ترسند، و حق هم دارند. عوض کردن تگ ایمیج بین دو نسخهٔ اصلی، داده را ارتقا نمی‌دهد، چون قالب ذخیره روی دیسک بین نسخه‌های اصلی عوض می‌شود. صفحهٔ نسخه‌بندی صریح می‌گوید: dump/reload یا pg_upgrade لازم است. نتیجه این است که در استکی که ۲۰۲۱ یا ۲۰۲۲ راه افتاده، ایمیج اپلیکیشن هر ماه بازسازی می‌شود و تگ دیتابیس سر جایش می‌ماند.

ایمیج رسمی Docker اما جلو می‌رود. فایل versions.json در مخزن docker-library/postgres، که فهرست نسخه‌هایی است که نگه‌دارندگان ایمیج می‌سازند، الان ۱۴ تا ۱۹ را پوشش می‌دهد. PostgreSQL 13 که نوامبر ۲۰۲۵ به پایان عمرش رسید، دیگر در آن نیست.

قدم ۱: همهٔ Postgres 14هایی را که اجرا می‌کنید پیدا کنید

به اسم ایمیج نمی‌شود اعتماد کرد. کانتینر ممکن است PostGIS، TimescaleDB، ایمیج یک فروشنده یا چیزی ساخت داخل تیم باشد که Postgres درونش است. به‌جایش از خود باینری بپرسید:

for c in $(docker ps --format '{{.Names}}'); do
  v=$(docker exec "$c" postgres --version 2>/dev/null) && echo "$c: $v"
done

این فقط کانتینرهای در حال اجرا را می‌بیند. پس فایل‌های compose روی دیسک را هم برای استک‌های متوقف یا روی سرورهای دیگر جست‌وجو کنید:

grep -rnE 'image: *"?postgres' --include='*compose*.y*ml' /srv /opt 2>/dev/null

برای هر نمونهٔ ۱۴، پیش از هر برنامه‌ریزی سه چیز را یادداشت کنید. هر کدام بعداً یک قدم را تعیین می‌کند (مثال‌ها کانتینری به اسم pg14 را فرض می‌کنند):

# data checksums: "version: 0" means off
docker exec -u postgres pg14 pg_controldata | grep -i checksum

# bootstrap superuser: empty output means "postgres"
docker exec pg14 printenv POSTGRES_USER

# where the data volume is mounted
docker inspect -f '{{range .Mounts}}{{.Source}} -> {{.Destination}}{{"\n"}}{{end}}' pg14

قدم ۲: نسخهٔ مقصد را انتخاب کنید

پیشنهاد ما PostgreSQL 18 است، نه ۱۹. نسخهٔ ۱۸ خط پایدار فعلی است (امروز در صفحهٔ نسخه‌بندی 18.6) و تا ۱۴ نوامبر ۲۰۳۰ پشتیبانی می‌شود. نسخهٔ 19.0 وقتی ۱۴ منقضی می‌شود یک هفته عمر دارد، و مهاجرتی که تاریخ پایان عمر به شما تحمیل کرده، زمان خوبی برای اولین استفاده‌کننده بودن نیست. الان به ۱۸ بروید و ۱۹ را برای سال بعد برنامه‌ریزی کنید، وقتی چند نسخهٔ فرعی‌اش منتشر شده باشد.

لازم نیست از ۱۵ و ۱۶ و ۱۷ عبور کنید. مستندات pg_upgrade می‌گوید از «9.2.X به بعد» پشتیبانی می‌کند و صفحهٔ نسخه‌بندی هم تأیید می‌کند که می‌شود نسخه‌ها را رد کرد، ولی توصیه می‌کند یادداشت‌های انتشار همهٔ نسخه‌های میانی را بخوانید. برای ۱۴ به ۱۸ یعنی یادداشت‌های ۱۵، ۱۶، ۱۷ و ۱۸، از فهرست ناسازگاری‌های بالای هر کدام.

قدم ۳: نسخهٔ پشتیبانی بگیرید که یک بار بازیابی‌اش کرده‌اید

docker exec pg14 pg_dumpall -U app | gzip > pg14-$(date +%F).sql.gz

آن را در یک کانتینر موقت بازیابی کنید و چند کوئری رویش بزنید. تا این کار را نکرده‌اید، این فقط یک فایل است، نه نسخهٔ پشتیبان.

روش الف: dump و restore

برای دیتابیس‌های تا چند گیگابایت ساده‌ترین راه همین است و در کنارش bloat جدول‌ها را هم پاک می‌کند. یک کانتینر postgres:18 با volume خالی خودش کنار قبلی بالا بیاورید و داده را منتقل کنید:

docker exec pg14 pg_dumpall -U app | docker exec -i pg18 psql -U app -q

وقتی این را اجرا کردیم، psql دو خطا چاپ کرد:

ERROR:  role "app" already exists
ERROR:  database "app" already exists

هر دو بی‌خطرند. کانتینر جدید آن نقش و دیتابیس را در اولین اجرا ساخته و بقیهٔ dump داخل همان‌ها بارگذاری می‌شود. هزینهٔ این روش زمان توقف است: نوشتن باید در کل مدت dump و restore متوقف باشد، پس با حجم داده بیشتر می‌شود.

روش ب: pg_upgrade با hard link

برای دیتابیس‌های بزرگ‌تر، pg_upgrade --link اسکیما را در کلاستر جدید می‌سازد و فایل‌های داده را به‌جای کپی، hard link می‌کند. مستندات pg_upgrade می‌گوید حالت link «خیلی سریع‌تر است (بدون کپی فایل)» و فضای دیسک کمتری می‌خواهد. این کار باینری هر دو نسخه را در یک جا لازم دارد که ایمیج رسمی ندارد. پروژهٔ tianon/docker-postgres-upgrade ایمیج‌هایی با تگ OLD-to-NEW منتشر می‌کند، از جمله 14-to-18. README خودش آن را یک proof of concept می‌داند که باید از آن «یاد گرفت» و برای خودتان تطبیقش دهید، نه ابزاری آماده، و تجربهٔ ما هم همین را نشان داد.

یک کلاستر آزمایشی 14.24 را که با POSTGRES_USER=app ساخته شده بود (همان شکلی که بیشتر فایل‌های compose تنظیم می‌کنند) به 18.6 ارتقا دادیم. سه بار شکست خورد تا درست شد، و هیچ‌کدام از این سه به تنظیمات خاص ما مربوط نبود.

خطای ۱: data checksums

old cluster does not use data checksums but the new one does
Failure, exiting

یادداشت‌های انتشار PostgreSQL 18 این را در ناسازگاری‌ها آورده: initdb حالا به‌طور پیش‌فرض data checksums را فعال می‌کند و «pg_upgrade تنظیم checksum یکسان در دو کلاستر را لازم دارد». کلاستری که ایمیج postgres:14 ساخته، checksum خاموش دارد مگر خودتان خواسته باشید. راه‌حل ساختن کلاستر جدید با --no-data-checksums است. اگر checksum می‌خواهید، بعداً با pg_checksums روی کلاستر متوقف روشنش کنید. مستنداتش هشدار می‌دهد که در کلاستر بزرگ این کار «ممکن است زمان زیادی ببرد» و در طول اجرا هیچ برنامهٔ دیگری نباید در پوشهٔ داده بنویسد.

خطای ۲: نام superuser

FATAL:  role "postgres" does not exist
could not connect to source postmaster started with the command: ...

ایمیج کمکی initdb را با superuser پیش‌فرض postgres اجرا می‌کند و با همین نام به کلاستر قدیمی وصل می‌شود. اگر در فایل compose مقدار POSTGRES_USER=app گذاشته‌اید، کلاستر قدیمی نقشی به اسم postgres ندارد. نام واقعی را هم به initdb و هم به pg_upgrade بدهید. با این دو اصلاح، اول حالت check را اجرا کنید که هیچ داده‌ای را تغییر نمی‌دهد:

# old cluster stopped, its files moved to /srv/pg/14/docker
docker run --rm \
  --mount type=bind,src=/srv/pg,dst=/var/lib/postgresql \
  -e PGDATAOLD=/var/lib/postgresql/14/docker \
  -e PGDATANEW=/var/lib/postgresql/18/docker \
  -e POSTGRES_INITDB_ARGS='--no-data-checksums --username=app' \
  tianon/postgres-upgrade:14-to-18 --check --username=app

وقتی *Clusters are compatible* چاپ شد، همان دستور را با --link --username=app به‌جای --check --username=app اجرا کنید.

خطای ۳: اپلیکیشن دیگر وصل نمی‌شود

ارتقا تمام شد و postgres:18 روی دادهٔ جدید بالا آمد. بعد یک کلاینت در کانتینر دیگر این را گرفت:

FATAL:  no pg_hba.conf entry for host "172.26.0.3", user "app", database "app", no encryption

ایمیج رسمی خطی به pg_hba.conf اضافه می‌کند که ورود با رمز از میزبان‌های دیگر را مجاز می‌کند، ولی فقط وقتی پوشهٔ دادهٔ خالی را مقداردهی اولیه می‌کند. این‌جا initdb داخل ایمیج کمکی اجرا شده، پس pg_hba.conf جدید فقط به اتصال‌های محلی اعتماد دارد. مستندات pg_upgrade در قدم «Restore pg_hba.conf» به این هشدار داده و اضافه می‌کند که postgresql.conf و postgresql.auto.conf هم ممکن است همین کار را لازم داشته باشند. اگر فایل‌های قدیمی را تغییر داده بودید، کپی‌شان کنید. اگر فقط پیش‌فرض‌های ایمیج را داشتید، آن خط را برگردانید:

docker exec pg18 sh -c 'echo "host all all all scram-sha-256" >> "$PGDATA/pg_hba.conf"'
docker exec pg18 psql -U app -c 'SELECT pg_reload_conf()'

مسیر volume در ایمیج ۱۸ عوض شده

از ۱۸ به بعد، ایمیج رسمی داده را در پوشه‌ای وابسته به نسخه، /var/lib/postgresql/18/docker، نگه می‌دارد و volume را روی /var/lib/postgresql تعریف می‌کند. مستندات ایمیج می‌گوید mountها باید به مسیر جدید اشاره کنند و pull request شمارهٔ 1259 دلیلش را توضیح می‌دهد: با یک mount برای هر دو نسخه، pg_upgrade --link بدون عبور از مرز فایل‌سیستم کار می‌کند. تعریف سرویس را مطابقش اصلاح کنید:

  db:
    image: postgres:18
    volumes:
      - /srv/pg:/var/lib/postgresql   # not /var/lib/postgresql/data

اگر فقط تگ را عوض کنید و mount قدیمی را نگه دارید، اسکریپت entrypoint نسخهٔ ۱۸ از اجرا خودداری می‌کند و این را چاپ می‌کند:

Error: in 18+, these Docker images are configured to store database data in a
       format which is compatible with "pg_ctlcluster" ...

شکست امنی است. ولی بهتر است آن را در اجرای آزمایشی ببینید تا ساعت دو بامداد.

بعد از ارتقا

طبق یادداشت‌های انتشار، pg_upgrade در PostgreSQL 18 آمار optimizer را نگه می‌دارد، ولی اجرای ما باز هم با پیام «Some statistics are not transferred by pg_upgrade» تمام شد و دو دستور vacuumdb پیشنهاد داد. آن‌ها را اجرا کنید و بعد دنبال رمزهایی بگردید که با MD5 ذخیره شده‌اند:

docker exec pg18 vacuumdb -U app --all --analyze-in-stages --missing-stats-only
docker exec pg18 vacuumdb -U app --all --analyze-only
docker exec pg18 psql -U app -Atc "SELECT rolname FROM pg_authid WHERE rolpassword LIKE 'md5%'"

یادداشت‌های انتشار PostgreSQL 14 هش پیش‌فرض رمز را به SCRAM-SHA-256 تغییر داد، ولی نقش‌هایی که از ۱۳ یا قدیمی‌تر منتقل شده‌اند ممکن است هنوز هش MD5 داشته باشند. PostgreSQL 18 احراز هویت MD5 را منسوخ کرده و می‌گوید پشتیبانی‌اش در یک نسخهٔ اصلی آینده حذف می‌شود. پس حالا که در پنجرهٔ نگه‌داری هستید، آن رمزها را عوض کنید.

دو نکتهٔ دیگر. در حالت link، طبق هشدار مستندات pg_upgrade، کلاستر قدیمی بعد از اجرای کلاستر جدید دیگر برای راه‌اندازی امن نیست، پس راه برگشت شما نسخهٔ پشتیبان قدم ۳ است، نه پوشهٔ قدیمی. آن پوشه را نگه دارید تا اپلیکیشن یک چرخهٔ کامل کاری روی ۱۸ کار کند، بعد با اسکریپت delete_old_cluster.sh که pg_upgrade به‌جا می‌گذارد پاکش کنید.

همان عادتی که Postgres را به‌روز نگه می‌دارد، برای هر ایمیج پایه‌ای که اجرا می‌کنید هم صدق می‌کند. این را برای PHP در PHP2Shell: وصلهٔ PHP زیر وردپرس نوشتیم، و طرف برنامه‌ریزی یک تاریخ قطعی پایان پشتیبانی را در برنامهٔ خروج از Exchange 2016/2019. اگر به هر حال سراغ مرتب کردن یک استک compose رفته‌اید، فرستادن لاگ کانتینرها به stdout هم اصلاح کوچکی است که می‌ارزد در همان دور انجام شود.

پرسش‌های متداول

آیا دیتابیس PostgreSQL 14 من در ۱۲ نوامبر ۲۰۲۶ از کار می‌افتد؟

نه. به کارش ادامه می‌دهد. پایان عمر یعنی پروژه دیگر اصلاحیه منتشر نمی‌کند، پس باگ‌های امنیتی و خرابی داده که بعد از آخرین انتشار ۱۴ پیدا شوند، وصله نمی‌شوند.

می‌شود مستقیم از ۱۴ به ۱۸ رفت؟

بله. pg_upgrade هر نسخهٔ مبدأ از 9.2 به بعد را پشتیبانی می‌کند و dump/restore بین هر دو نسخه‌ای کار می‌کند. اول یادداشت‌های انتشار ۱۵، ۱۶، ۱۷ و ۱۸ را برای ناسازگاری‌ها بخوانید.

بهتر نیست منتظر PostgreSQL 19 بمانم؟

ما نمی‌مانیم. انتشار ۱۹ برای ۱۹ نوامبر ۲۰۲۶ برنامه‌ریزی شده، یعنی بعد از پایان پشتیبانی ۱۴. ارتقا به ۱۸ الان و به ۱۹ بعداً، دو کار برنامه‌ریزی‌شده است به‌جای یک کار عجولانه.

عوض کردن تگ ایمیج از ۱۴ به ۱۸ کافی است؟

نه. پوشهٔ داده باید با pg_upgrade یا dump/restore ارتقا پیدا کند. با mount به سبک قدیم، ایمیج ۱۸ اجرا نمی‌شود و شما را به pg_upgrade ارجاع می‌دهد.

منابع: سیاست نسخه‌بندی PostgreSQL، اطلاعیهٔ PostgreSQL 19 RC 1، یادداشت‌های انتشار PostgreSQL 18، یادداشت‌های انتشار PostgreSQL 14، مستندات pg_upgrade، تقویم انتشار Amazon RDS for PostgreSQL، مستندات ایمیج postgres و tianon/docker-postgres-upgrade. دستورها در ۱ نوامبر ۲۰۲۶ روی Docker با postgres:14 (14.24) و postgres:18 (18.6) آزموده شدند.

هنوز PostgreSQL 14 را در production اجرا می‌کنید؟

ارتقای دیتابیس استک‌های خودمیزبان را برنامه‌ریزی و اجرا می‌کنیم: نسخهٔ پشتیبانِ آزموده، تمرین روی یک کپی، و پنجرهٔ جابه‌جایی که از قبل با هم توافق کرده‌ایم.

خدمات ابر و DevOps

اشتراک‌گذاری این مقاله