SSD Nodes Learn 🎉 VPS $5.50/माह से
गाइड Matt Connorलेखक: Matt Connor

Docker में डेटाबेस चलाएं या host पर? सही तरीका

Docker में PostgreSQL, MySQL, MongoDB या Redis चलाना सुरक्षित है। डेटा वॉल्यूम, बैकअप, अपग्रेड और मेमोरी लिमिट्स को मैनेज करने के सही तरीके यहाँ विस्तार से जानें।

क्या डेटाबेस को Docker में चलाना चाहिए या host पर?

डेटाबेस को Docker में चलाएं। एक VPS पर एक application stack के लिए, containerised PostgreSQL, MySQL, MongoDB या Redis का उपयोग करना एक सामान्य production विकल्प है, और इसे लेकर लोगों के बीच होने वाली बहस अक्सर गलत होती है। एक container, namespaces और cgroups के साथ एक Linux process है, न कि virtual machine, इसलिए डेटाबेस और डिस्क के बीच कोई hypervisor नहीं होता है। Bind mount या local named volume के साथ, read और write operations सीधे host filesystem पर होते हैं, वही filesystem जिसका उपयोग package install करने पर किया जाता है।

असली लागत परिचालन (operational) संबंधी है। चार चीजें तय करती हैं कि यह सेटअप ठीक है या एक धीमी आपदा: डेटा कहाँ रहता है, उस directory का मालिक कौन है, major version upgrade कैसा दिखता है, और क्या आपने कभी backup restore किया है। यदि आप इन्हें सही रखते हैं, तो container केवल एक विवरण (detail) है। यदि आप इन्हें गलत करते हैं, तो container ही वह चीज होगी जिसे आप दोष देंगे।

हर server database के लिए यही निर्णय लागू होता है। नीचे दिए गए उदाहरण PostgreSQL, MySQL, MongoDB और Redis का उपयोग करते हैं, और जहाँ आवश्यक है वहाँ product-specific अंतर बताए गए हैं।

कंटेनर वास्तव में क्या बदलता है

स्टोरेज पाथ नहीं, जब तक आप उसे माउंट न करें। वही कर्नल, वही पेज कैश, वही फाइलसिस्टम।

प्रदर्शन (performance) में एक वास्तविक समस्या तब आती है जब आप कुछ भी माउंट नहीं करते हैं। बिना वॉल्यूम के, डेटा डायरेक्टरी कंटेनर की राइटेबल लेयर में चली जाती है, जो इमेज के ऊपर एक ओवरले फाइलसिस्टम होती है। वहां लिखना धीमा होता है, और कंटेनर हटाए जाने पर पूरी लेयर डिलीट हो जाती है। "आज सुबह मेरा डेटाबेस खाली था" जैसी समस्या यहीं से आती है।

जो वास्तव में बदलता है:

  • लाइफसाइकिल। docker compose down कंटेनर को नष्ट कर देता है। जो कुछ भी वॉल्यूम में नहीं था, वह उसके साथ चला जाता है।
  • वर्जन। इमेज टैग ही वर्जन है। डेटाबेस कंटेनर के अंदर ऐसा कोई apt upgrade नहीं होता जो अगले docker compose pull के बाद भी सुरक्षित रहे।
  • मेमोरी अकाउंटिंग। cgroup लिमिट कर्नल द्वारा लागू की गई एक सख्त दीवार है, और डेटाबेस को इसके बारे में पता नहीं होता है।
  • यूजर। प्रोसेस कंटेनर के अंदर एक न्यूमेरिक यूजर आईडी के रूप में चलती है, जिसका आपके होस्ट पर शायद कोई स्वामित्व न हो।

डेटा कहाँ रहता है, यह सब कुछ तय करता है

इसके दो अच्छे विकल्प हैं और एक सामान्य गलती है।

  • एक named volume: pgdata:/var/lib/postgresql/data। Docker /var/lib/docker/volumes/<project>_pgdata/_data पर डायरेक्टरी बनाता है, और image entrypoint पहली बार चलने पर इसका स्वामित्व (ownership) सेट करता है। यह डिफ़ॉल्ट विकल्प है।
  • एक bind mount: /srv/appname/pg:/var/lib/postgresql/data। आप पाथ चुनते हैं, इसलिए अनुमतियों (permissions) की समस्या आपकी जिम्मेदारी है।
  • कोई माउंट नहीं। ऊपर देखें। डेटा कंटेनर के अंदर रहता है।

इसका पूरा ट्रेड-ऑफ अपने आप में एक विषय है, और bind mounts against named volumes इसे कवर करता है। डेटाबेस के लिए संक्षिप्त उत्तर यह है: named volume का उपयोग करें जब तक कि आपके पास होस्ट पाथ जानने का कोई विशिष्ट कारण न हो, और यदि आप bind mount का उपयोग करते हैं, तो इसे /srv/appname/pg जैसे किसी स्थिर स्थान पर रखें, न कि प्रोजेक्ट डायरेक्टरी के अंदर जहाँ git clean इसे एक्सेस कर सके।

एक सख्त सीमा: डेटाबेस डेटा डायरेक्टरी को NFS (नेटवर्क फ़ाइल सिस्टम) या किसी ऐसे नेटवर्क माउंट पर न रखें जिसके लॉकिंग और fsync व्यवहार का आपने परीक्षण नहीं किया है। डेटाबेस यह मानकर चलते हैं कि एक सफल fsync का मतलब है कि बाइट्स स्थिर स्टोरेज पर हैं। जब यह धारणा गलत होती है, तो डेटा करप्शन होता है जो हफ्तों बाद दिखाई देता है।

वॉल्यूम के गायब होने से पहले उसका नाम पिन करें

Compose वॉल्यूम का नाम <project>_<volume> रखता है, और प्रोजेक्ट का नाम डिफ़ॉल्ट रूप से डायरेक्टरी के नाम पर आधारित होता है। इसलिए वॉल्यूम की पहचान एक डायरेक्टरी नाम पर निर्भर करती है, जिसे लोग बिना सोचे-समझे बदल देते हैं।

/srv/app को /srv/app-old पर ले जाने, या compose फ़ाइल में pgdata की को रीनेम करने पर, अगला docker compose up -d एक बिल्कुल नया खाली वॉल्यूम बना देता है। Postgres इसमें एक नया क्लस्टर इनिशियलाइज़ कर देता है। कंटेनर हेल्दी होता है, एप्लिकेशन स्टार्ट हो जाती है, और सभी टेबल गायब हो जाते हैं। अच्छी खबर यह है कि पुराना वॉल्यूम अभी भी डिस्क पर पुराने नाम के साथ मौजूद रहता है।

docker volume ls
docker volume inspect app_pgdata

नामों को पिन करें ताकि ऐसा न हो सके। प्रोजेक्ट का नाम और वॉल्यूम का नाम स्पष्ट रूप से सेट करें:

name: myapp

services:
  db:
    image: postgres:17
    volumes:
      - pgdata:/var/lib/postgresql/data

volumes:
  pgdata:
    name: myapp_pgdata

यदि कोई पुराना वॉल्यूम पहले से ही आपका डेटा रखे हुए है, तो डेटाबेस को रोककर उसे कॉपी करें:

docker compose stop db
docker run --rm -v app_pgdata:/from -v myapp_pgdata:/to alpine sh -c 'cp -a /from/. /to/'
docker compose start db

यदि आप डेटाबेस के चलते समय इसे कॉपी करते हैं, तो आपको उन फ़ाइलों की अधूरी कॉपी मिलेगी जो लिखी जा रही थीं। पहले इसे रोकें।

डेटा डायरेक्टरी का स्वामी कौन है

आधिकारिक Postgres, MySQL और MongoDB इमेजेस अपने सर्वर को एक unprivileged user id के साथ चलाती हैं, जो आमतौर पर 999 होती है। जब कंटेनर root के रूप में शुरू होता है, तो entrypoint डेटा डायरेक्टरी का स्वामित्व उस यूजर को दे देता है और फिर privileges को त्याग देता है। यही कारण है कि एक खाली bind mount आमतौर पर पहली बार में ही काम कर जाता है।

जैसे ही आप compose फाइल में user: सेट करते हैं, यह प्रक्रिया विफल हो जाती है, क्योंकि तब entrypoint के पास किसी भी चीज को ठीक करने के लिए कोई privilege नहीं बचती। Postgres इसे सीधे तौर पर बताता है:

initdb: error: could not change permissions of directory "/var/lib/postgresql/data": Operation not permitted

गलत mode के साथ मौजूद डेटा डायरेक्टरी एक अलग संदेश देती है, और इसे पहचानना महत्वपूर्ण है क्योंकि इसका समाधान chmod है, न कि chown:

FATAL:  data directory "/var/lib/postgresql/data" has invalid permissions
DETAIL:  Permissions should be u=rwx (0700) or u=rwx,g=rx (0750).

root-owned bind mount पर MongoDB lock फाइल पर विफल हो जाता है:

Unable to create/open the lock file: /data/db/mongod.lock (Permission denied). Ensure the user executing mongod is the owner of the lock file and has the appropriate permissions.

इसका समाधान होस्ट डायरेक्टरी को नाम के बजाय numeric id पर chown करना है:

sudo chown -R 999:999 /srv/appname/pg
sudo chmod 700 /srv/appname/pg
ls -ldn /srv/appname/pg

ls -ldn नामों के बजाय नंबर प्रिंट करता है, और इसे 999 999 दिखाना चाहिए। आपके होस्ट पर postgres नामक अकाउंट और इमेज के अंदर postgres नामक अकाउंट का आपस में कोई संबंध नहीं है: kernel नंबरों की तुलना करता है, और नाम दोनों तरफ अलग-अलग देखे जाते हैं। PUID और PGID कैसे होस्ट यूजर्स को कंटेनर में मैप करते हैं इस मैपिंग को विस्तार से समझाता है। rootless Docker या user namespace remapping के तहत नंबर फिर से बदल जाते हैं, इसलिए 999 मान लेने के बजाय चलते हुए कंटेनर से ids को पढ़ें।

Named volumes इस पूरे सेक्शन को पहली बार चलने पर अनावश्यक बना देते हैं, क्योंकि Docker एक खाली डायरेक्टरी बनाता है और entrypoint उसका स्वामी बन जाता है।

अपग्रेड: इमेज टैग बदलने के माध्यम से पैकेज अपग्रेड

होस्ट पर, apt upgrade आपको माइनर वर्ज़न में आगे ले जाता है। आपका डिस्ट्रीब्यूशन आपके डेटाबेस का मेजर वर्ज़न अपने आप नहीं बदलेगा, और जब आप वर्ज़न बदलने का निर्णय लेते हैं, तो दोनों बाइनरी सेट एक साथ इंस्टॉल किए जा सकते हैं, जो कि ठीक वही है जिसकी pg_upgrade को आवश्यकता होती है।

कंटेनर में टैग ही वर्ज़न होता है, इसलिए अपग्रेड का मतलब केवल एक लाइन को एडिट करना है। यह माइनर अपग्रेड को आसान और मेजर अपग्रेड को एक प्रक्रिया बना देता है।

postgres:16 को postgres:17 में बदलें, docker compose up -d चलाएं, और कंटेनर तुरंत बंद हो जाएगा:

PostgreSQL Database directory appears to contain a database; Skipping initialization
FATAL:  database files are incompatible with server
DETAIL:  The data directory was initialized by PostgreSQL version 16, which is not compatible with this version 17.

कुछ भी क्षतिग्रस्त नहीं होता है। नई बाइनरी पुराने ऑन-डिस्क कैटलॉग लेआउट को पढ़ने से मना कर देती हैं, जो मेजर वर्ज़न के बीच बदल जाता है। टैग को वापस postgres:16 पर सेट करें और यह फिर से शुरू हो जाएगा। वह रोलबैक ही वह एकमात्र वास्तविक अपग्रेड लाभ है जो कंटेनर आपको देते हैं।

समर्थित मार्ग डंप और रिस्टोर (dump and restore) है। PostgreSQL यह पसंद करता है कि डंप नए क्लाइंट द्वारा लिया जाए, इसलिए इसे compose नेटवर्क पर अभी भी चल रहे पुराने सर्वर के विरुद्ध नई इमेज से चलाएं:

docker run --rm --network myapp_default -e PGPASSWORD="$POSTGRES_PASSWORD" \
  postgres:17 pg_dumpall -h db -U postgres > /srv/backups/all.sql
ls -lh /srv/backups/all.sql
tail -n 2 /srv/backups/all.sql

फ़ाइल कम से कम दसियों किलोबाइट की होनी चाहिए और अंत में PostgreSQL database cluster dump complete लिखी हुई लाइन होनी चाहिए। कुछ सौ बाइट्स की फ़ाइल का मतलब है कि डंप विफल हो गया है और आप बिना किसी कारण के वॉल्यूम डिलीट करने वाले हैं। उस जांच के बाद ही:

docker compose down
docker volume rm myapp_pgdata
# edit the compose file: image: postgres:17
docker compose up -d db
docker compose exec -T db psql -U postgres -f /dev/stdin < /srv/backups/all.sql

अन्य इंजन अलग हैं:

  • MySQL 8 स्टार्टअप पर अपने डेटा डिक्शनरी को अपग्रेड करता है, इसलिए माइनर टैग बदलाव आमतौर पर केवल एक रीस्टार्ट है। रिलीज़ सीरीज़ के बीच जंप करने से पहले रिलीज़ नोट्स पढ़ें, और किसी भी तरह से पहले डंप लें।
  • MariaDB अपेक्षा करता है कि नए वर्ज़न पर सर्वर के आने के बाद mariadb-upgrade चलाया जाए।
  • MongoDB को एक बार में एक मेजर वर्ज़न अपग्रेड किया जाना चाहिए, और प्रत्येक चरण के बाद आगे बढ़ने से पहले आपको फीचर कम्पैटिबिलिटी वर्ज़न सेट करना होगा। वर्ज़न छोड़ने का मतलब है कि mongod शुरू होने से मना कर देगा और एक UPGRADE PROBLEM लाइन लॉग करेगा जिसमें featureCompatibilityVersion का नाम होगा। MongoDB 7.0 के बाद से कमांड को एक स्पष्ट पुष्टिकरण फ्लैग की आवश्यकता होती है: db.adminCommand({ setFeatureCompatibilityVersion: "8.0", confirm: true })
  • Redis पुरानी स्नैपशॉट फ़ाइलों को आसानी से लोड कर लेता है लेकिन नई फ़ाइलों को नहीं, इसलिए अपग्रेड एक रीस्टार्ट है और डाउनग्रेड डेटा लोड करने में विफल हो सकता है।

सामान्य नियम: कंटेनर डाउनग्रेड को आसान बनाता है और अपग्रेड को बिल्कुल भी आसान नहीं बनाता है।

मेरा database container exit code 137 के साथ बंद क्यों हो जाता है?

इसका कारण kernel का out of memory (OOM) killer है जिसने इसे बंद कर दिया है। 137 का मतलब 128 जमा signal 9 है।

docker compose ps
docker inspect myapp-db-1 | grep -i oomkilled
journalctl -k | tail -n 20

docker compose ps चलाने पर Exited (137) दिखाई देता है, inspect लाइन में "OOMKilled": true लिखा होता है, और kernel log में एक मेल खाती हुई entry होती है:

Memory cgroup out of memory: Killed process 4711 (postgres) total-vm:2170416kB

इसकी कार्यप्रणाली लोगों को हैरान कर देती है। PostgreSQL और MySQL अपने buffers का आकार उस memory के आधार पर तय करते हैं जो host कुल memory के रूप में रिपोर्ट करता है। cgroup limit उनके लिए उस संख्या को नहीं बदलती। 16 GB वाले host पर 2 GB की limit होने पर, database ऐसे योजना बनाता है जैसे उसके पास 16 GB हो, और host पर दबाव आने से बहुत पहले ही cgroup उसे मार देता है। इसलिए केवल memory limit पर्याप्त नहीं है। आपको database को यह भी बताना होगा कि उसके पास कितनी memory उपलब्ध है:

  • PostgreSQL: shared_buffers सेट करें, और work_mem पर ध्यान दें। work_mem प्रति connection, प्रति sort operation आवंटित होता है, इसलिए एक उदार मान को पचास connections से गुणा करना अक्सर उस container के बंद होने का कारण बनता है जो startup के बजाय load के दौरान मर जाता है।
  • MySQL और MariaDB: innodb_buffer_pool_size सेट करें, जो default रूप से 128M होता है। container में innodb_dedicated_server को बंद रखें, क्योंकि इसका मुख्य काम detected machine memory से खुद का आकार तय करना है।
  • MongoDB: host memory से अनुमान लगाने देने के बजाय WiredTiger cache size को स्पष्ट रूप से सेट करें।
  • Redis: maxmemory default रूप से unlimited होता है, इसलिए Redis तब तक बढ़ता है जब तक cgroup उसे रोक न दे। maxmemory को container limit से काफी नीचे सेट करें और एक maxmemory-policy चुनें।

Postgres भी अपनी तरफ से इस घटना की रिपोर्ट करता है, और log में आपको ये दो लाइनें मिलेंगी:

LOG:  server process (PID 123) was terminated by signal 9: Killed
LOG:  terminating any other active sessions due to crash of another server process

एक backend के बंद होने से बाकी सभी backends को restart होना पड़ता है, क्योंकि shared memory अब असंगत (inconsistent) हो सकती है। यह आपके application के लिए एक connection storm है, न कि कोई सामान्य घटना। Docker Compose में memory limits सेट करना इसके syntax और mem_limit तथा deploy.resources प्रारूप के बीच के अंतर को कवर करता है।

इनमें से कुछ भी host पर खत्म नहीं होता। यह बस स्थानांतरित हो जाता है। cgroup के बिना, database box पर मौजूद बाकी सभी चीजों के साथ प्रतिस्पर्धा करता है, और host OOM killer score के आधार पर किसी एक को चुनता है, जो sshd भी हो सकता है। एक ऐसी limit जो database को अनुमानित तरीके से बंद कर दे, उस host OOM से बेहतर है जो आपको ही बाहर (lock out) कर दे।

Backups: dump inside, back up outside

चलते हुए database के data directory को copy करके उसका backup न लें। यदि server लिखते समय file-level copy ली जाती है, तो वह एक torn copy होती है, और इसका पता आपको restore करते समय चलता है।

दो सही तरीके हैं: database के चलते समय उसके अपने tool से dump लें और उस dump का backup लें, या container को रोककर volume की cold copy बनाएँ।

docker compose exec -T db pg_dump -U postgres -Fc appdb > /srv/backups/appdb.dump
docker compose exec -T db mysqldump -u root -p"$MYSQL_ROOT_PASSWORD" --single-transaction --all-databases > /srv/backups/mysql.sql
docker compose exec -T db mongodump --archive --gzip --db appdb > /srv/backups/appdb.archive.gz
docker compose exec -T redis redis-cli BGSAVE

-T महत्वपूर्ण है। इसके बिना, docker compose exec कमांड के साथ terminal attach कर सकता है, और terminal layer output stream में carriage returns जोड़ देता है। ऐसा text dump restore करते समय अजीब errors देता है और binary dump पूरी तरह corrupt हो जाता है। यह backup के समय चुपचाप विफल हो जाता है और एक महीने बाद बड़ी समस्या खड़ी करता है।

--single-transaction पूरे server को lock किए बिना mysqldump को InnoDB tables का एक consistent snapshot देता है।

ये कमांड्स प्रत्येक एक file लिखती हैं। ये कोई backup system नहीं हैं: इनमें न तो retention है, न off-box copy है, और न ही verification। dump directory को ऐसे tool को सौंपें जो ये तीनों काम करे, जिसके लिए restic backups from a VPS का उपयोग किया जाता है। /var/lib/docker/volumes का नहीं, बल्कि /srv/backups का backup लें।

इसके बाद restore चलाकर देखें, क्योंकि जिस backup को आपने कभी restore नहीं किया, वह backup नहीं है:

docker compose exec -T db createdb -U postgres restore_test
docker compose exec -T db pg_restore -U postgres -d restore_test < /srv/backups/appdb.dump
docker compose exec -T db psql -U postgres -d restore_test -c '\dt'

\dt को आपके application की tables की सूची दिखानी चाहिए। यदि परिणाम खाली है, या Did not find any relations. आता है, तो इसका मतलब है कि dump वैसा नहीं है जैसा आप सोच रहे हैं। काम पूरा होने पर restore_test को drop कर दें।

वह कमांड जो सब कुछ हटा देती है

docker compose down -v.

साधारण down कंटेनरों और नेटवर्क को हटा देता है। -v उस compose फाइल में घोषित प्रत्येक named volume और उन कंटेनरों से जुड़े हर anonymous volume को भी हटा देता है। इसमें कोई प्रॉम्प्ट नहीं आता और इसे वापस (undo) नहीं किया जा सकता। यह self-hosted डेटाबेस को नष्ट करने का सबसे आम तरीका है, और यह आमतौर पर किसी असंबंधित समस्या के निवारण (troubleshooting) के दौरान होता है, क्योंकि किसी फोरम के उत्तर में इसे चलाने के लिए कहा गया होता है।

चार चीजें नुकसान के दायरे (blast radius) को कम करती हैं:

  • डेटाबेस वॉल्यूम को external: true के रूप में घोषित करें। Compose उस वॉल्यूम को नहीं हटाएगा जिसका वह स्वामी नहीं है, इसलिए -v उस तक नहीं पहुँच पाएगा। आप इसे docker volume create myapp_pgdata के साथ एक बार बनाते हैं।
  • नियमित रीस्टार्ट के लिए docker compose stop और docker compose start का उपयोग करें। Compose में stop बनाम down विस्तार से बताता है कि प्रत्येक क्या हटाता है।
  • डंप (dumps) को compose द्वारा प्रबंधित प्रत्येक वॉल्यूम के बाहर एक होस्ट पाथ पर रखें।
  • किसी ऐसे स्टैक में, जिसमें आपका महत्वपूर्ण डेटा हो, कभी भी किसी ट्रबलशूटिंग उत्तर से -v को पेस्ट न करें।

डेटाबेस पोर्ट को पब्लिक न करें

यह लाइन आपके डेटाबेस को पब्लिक इंटरनेट पर डाल देती है:

    ports:
      - "5432:5432"

यह हर इंटरफेस पर bind हो जाता है। Docker आपके फायरवॉल के input rules के देखने से पहले ही पैकेट के डेस्टिनेशन को rewrite करके पोर्ट पब्लिश कर देता है, और ufw के नियम input chain में होते हैं, इसलिए ufw deny 5432 का कोई असर नहीं पड़ता। Docker द्वारा पब्लिश किए गए पोर्ट ufw को क्यों बायपास करते हैं chain traversal को दर्शाता है।

एक ही compose प्रोजेक्ट में मौजूद एप्लिकेशन, compose नेटवर्क पर सर्विस नाम के जरिए डेटाबेस तक पहुँच जाती है, इसलिए उसे किसी पब्लिश पोर्ट की आवश्यकता नहीं होती। इस ब्लॉक को हटा दें। यदि आप होस्ट पर किसी क्लाइंट का उपयोग करना चाहते हैं, तो केवल loopback पर bind करें:

    ports:
      - "127.0.0.1:5432:5432"

जाँचें कि वास्तव में कौन सा पोर्ट listening है:

sudo ss -ltnp | grep 5432

127.0.0.1:5432 वह है जो आपको चाहिए। 0.0.0.0:5432 का मतलब है कि कोई भी आपके पासवर्ड का प्रयास कर सकता है।

कहाँ क्या चलाएं

एक VPS पर एक application. Container का उपयोग करें। एक निश्चित नाम वाला named volume रखें, कोई port publish न करें, memory limit सेट करें जो database settings के अनुरूप हो, और एक nightly dump को host path पर रखें जिसे restic collect करे। VPS पर एक clean Docker install से शुरुआत करें और पूरे stack को एक compose file में रखें जिसे आप commit करते हैं। इसका लाभ स्पष्ट है: database version git में एक reviewable line बन जाता है।

कई services चलाने वाला एक host. Containers का उपयोग करें, हर application के लिए एक अलग database, न कि सभी के लिए एक साझा सर्वर। एक साझा सर्वर हर application को एक ही upgrade schedule से जोड़ देता है, और एक runaway query सभी के लिए outage का कारण बन सकती है। हर container को उसकी अपनी memory limit दें ताकि एक खराब query उसी app तक सीमित रहे जिसने उसे लिखा है। कई छोटे Postgres instances में disk का उपयोग थोड़ा अधिक होता है, लेकिन coordination बहुत कम लगता है।

database ही product है. इसे vendor के package repository से host पर चलाएं, या किसी managed service के लिए भुगतान करें। pg_upgrade को एक ही समय पर binaries के दोनों major versions की आवश्यकता होती है, जो packages आपको देते हैं और एक single-version image नहीं दे पाती। जब database का मशीन और उसके disks पर पूरा नियंत्रण होता है, तो replication और WAL (write ahead log) archiving के साथ point in time recovery आसान हो जाता है। उस सिस्टम के लिए हमेशा boring रास्ता चुनें जो आपको रात के 03:00 बजे जगा सकता है।

application छोटी है. बिना किसी सर्वर database के चलाने पर विचार करें। एक VPS पर single-writer web application के लिए अक्सर VPS पर production में SQLite का उपयोग करना बेहतर होता है, जहाँ backup केवल एक file है और upgrade का रास्ता एक library version है।

FAQ

क्या production database को Docker में चलाना सुरक्षित है?

हाँ, single-server application stack के लिए यह सुरक्षित है। एक container वास्तव में namespaces और cgroups के साथ एक Linux process है। यदि आप volume mount करते हैं, तो database उसी host filesystem पर लिखता है जिसका उपयोग वह package install करने पर करता। जोखिम गति के बजाय परिचालन संबंधी (operational) होते हैं: जैसे कि ऐसी volume जिसका नाम pinned न हो, गलत user id के स्वामित्व वाला bind mount, या ऐसा restore जिसे आपने कभी test न किया हो, और docker compose down -v। इन चार बिंदुओं को ठीक रखें, तो container सुरक्षित है। जब database मुख्य workload बन जाए और आपको pg_upgrade, replication या point in time recovery की आवश्यकता हो, तब host install पर shift करें।

क्या मुझे database data के लिए bind mount का उपयोग करना चाहिए या named volume का?

जब तक आपके पास host path जानने का कोई विशेष कारण न हो, named volume का ही उपयोग करें। Docker directory बनाता है और image entrypoint पहली बार start होने पर ownership set कर देता है, इसलिए permission की समस्या नहीं आती। volume को एक स्पष्ट name: के साथ pin करें या उसे external: true के रूप में mark करें, अन्यथा project directory का नाम बदलने पर चुपचाप एक नई खाली volume और खाली database बन जाएगी। यदि आप host directory को उस numeric user id पर chown करते हैं जिस पर image चलती है (official Postgres, MySQL और MongoDB images के लिए यह 999 है), तो bind mount ठीक है। इसे ls -ldn के साथ verify करें, क्योंकि ls -l उस संख्या के लिए आपके host का नाम दिखाता है और वह नाम container के अंदर अर्थहीन होता है।

docker compose down -v क्या delete करता है?

यह साधारण down की तरह containers और network को हटा देता है, और -v इसके अतिरिक्त उस compose file में घोषित हर named volume और उन containers से जुड़ी हर anonymous volume को भी हटा देता है। इसमें database भी शामिल है। इसके लिए कोई confirmation prompt नहीं आता और कोई recovery संभव नहीं है। external: true के रूप में mark की गई volumes हटाई नहीं जातीं, और यही कारण है कि database volume को external mark करना सबसे अच्छा है। नियमित restart के लिए docker compose stop और docker compose start का उपयोग करें।

Docker में PostgreSQL को नए major version पर कैसे upgrade करें?

Dump और restore करें। postgres:16 को postgres:17 में बदलकर restart करने पर FATAL: database files are incompatible with server मिलता है, जिसमें एक DETAIL लाइन दोनों versions का नाम बताती है, क्योंकि नई binaries पुराने catalog layout को नहीं पढ़ पाएंगी। कुछ भी खराब नहीं होता: पुराना tag वापस लगाने पर यह फिर से start हो जाता है। चल रहे पुराने container के विरुद्ध नए version के client का उपयोग करके pg_dumpall लें, पुष्टि करें कि file PostgreSQL database cluster dump complete के साथ समाप्त होती है, फिर एक खाली volume पर नया tag लाएं और dump load करें। एक ही major version के भीतर minor upgrades के लिए केवल pull और restart की आवश्यकता होती है।

मेरा database container exit code 137 के साथ क्यों बंद हो जाता है?

137 का अर्थ है 128 जमा signal 9, जिसका मतलब है कि किसी चीज़ ने process को तुरंत मार दिया। docker inspect <container> | grep -i oomkilled चलाएं; true मान का अर्थ है कि container अपनी cgroup memory limit तक पहुँच गया है। इसका सामान्य कारण यह है कि PostgreSQL और MySQL host से कुल memory पढ़ते हैं और container की limit को नहीं देख पाते, इसलिए वे 2 GB के भीतर रहते हुए भी 16 GB के लिए योजना बनाते हैं। container को दी गई limit के अनुसार shared_buffers और work_mem, या innodb_buffer_pool_size सेट करें। यह पुष्टि करने के लिए कि kernel ने किस process को चुना, journalctl -k में संबंधित Memory cgroup out of memory लाइन की जाँच करें।