Mert Özen Her Satırda Daha İleri
Veritabanı MongoDB — #02

MongoDB Lab'ı: Docker'da Tek Node'lu Replica Set, mongosh, Compass ve Bookstore Verisi

Mert Özen 14 Kas 2026 14 dk 6 görüntülenme
MongoDB Lab'ı: Docker'da Tek Node'lu Replica Set, mongosh, Compass ve Bookstore Verisi

Geliştirme için en doğru MongoDB kurulumu, parola korumalı ve 127.0.0.1'e açık tek node'lu bir replica set'tir; transaction ve change stream ancak replica set'te çalışır, member adı da host'tan çözülebilir olmalıdır. Gerçek çıktılarla compose dosyası ve keyfile, rs.initiate, kullanıcı ve yetkiler, mongosh, Bookstore verisinin mongoimport ile yüklenmesi, .NET'ten bağlantı ve standalone sunucuda transaction hatası.

Sürüm notu: Bu yazıdaki örnekler MongoDB 9.0.8 Community Server (mongodb/mongodb-community-server:9.0-ubuntu2204), mongosh 2.6, MongoDB Database Tools 100.20 (mongoimport), .NET 10 ve MongoDB.Driver 3.14 ile çalıştırılmıştır. Aynı compose dosyası 8.0-ubuntu2204 etiketiyle MongoDB 8.0.38'de de aynı sonucu verdi.

Önce Tahmin Et

Bir ekip arkadaşın Bookstore (sitedeki serilerin ortak örnek uygulaması, bir online kitapçı) için Docker'da MongoDB'yi --replSet rs0 ile başlatmış ve replica set'i dokümanlardaki en kısa komutla, parametresiz rs.initiate() ile kurmuş:

rs.initiate()
{
  info2: 'no configuration specified. Using a default configuration for the set',
  me: '296a64f2e716:27017',
  ok: 1
}

Container'ın 27017 portu host'a açık ve container'ın içinden rs.status() primary'yi gösteriyor. Şimdi .NET uygulaması Windows'taki makinesinden bağlanıyor; connection string replica set adını da içeriyor: mongodb://...@localhost:27017/?replicaSet=rs0&authSource=admin. Bağlantı kurulur mu?

TimeoutException after 5021 ms
A timeout occurred after 5001ms selecting a server using CompositeServerSelector{ ... }.
Client view of cluster state is { ClusterId : "1", Type : "ReplicaSet", State : "Disconnected",
Servers : [{ ServerId: "{ ClusterId : 1, EndPoint : "Unspecified/296a64f2e716:27017" }",
EndPoint: "Unspecified/296a64f2e716:27017", ReasonChanged: "Heartbeat", State: "Disconnected", ...
HeartbeatException: "MongoDB.Driver.MongoConnectionException: An exception occurred while opening
a connection to the server. ---> System.Net.Sockets.SocketException ...

Bağlanamadı ve hata mesajında hiç yazmadığımız bir adres var: 296a64f2e716:27017. Driver localhost:27017'ye ulaştı, ama replica set'e bağlanan bir istemci yalnız verdiğin adresi kullanmaz; sunucudan replica set'in member listesini öğrenir ve bundan sonra o adreslerle konuşur. Parametresiz rs.initiate() member adı olarak container'ın hostname'ini, yani container ID'sini yazdı. Bu ad container'ın içinde çözülüyor, host'ta çözülmüyor. Bu yazıda lab'ı bu ve benzeri tuzaklara düşmeden kuruyoruz: compose dosyası, keyfile, replica set, kullanıcılar, mongosh, Bookstore verisinin yüklenmesi ve .NET'ten bağlantı.

Neden Tek Node'lu Bir Replica Set?

Geliştirme makinesinde tek bir mongod yeterli görünür, ama standalone bir sunucu MongoDB'nin bazı özelliklerini hiç çalıştırmaz. Multi-document transaction'lar ve change stream'ler yalnız replica set ya da sharded cluster'da var. Standalone bir sunucuda transaction başlatmayı deneyelim:

// 02 - multi-document transaction: replica set ister
const session = db.getMongo().startSession();
const orders = session.getDatabase("bookstore").getCollection("tx_demo");
try {
    session.startTransaction();
    orders.insertOne({ _id: 1, note: "inside a transaction" });
    session.commitTransaction();
    print("committed, documents:", db.getSiblingDB("bookstore").tx_demo.countDocuments());
} catch (e) {
    print(e.name + ":", e.message);
} finally {
    db.getSiblingDB("bookstore").tx_demo.drop();
    session.endSession();
}
MongoServerError: Transaction numbers are only allowed on a replica set member or mongos

Aynı script, aşağıda kuracağımız lab'da committed, documents: 1 yazıyor. Uygulama kodun ileride transaction ya da change stream kullanacaksa (10. yazıda ikisini de kullanacağız), geliştirme ortamı production'la aynı topolojide olmalı. Tek node'lu replica set bunun en ucuz yolu: tek bir container, tek bir volume, ama sunucu kendini replica set üyesi olarak görüyor, oplog tutuyor ve majority write concern'ü tek üyeyle sağlıyor. Yüksek erişilebilirlik vermez; onun için en az üç üye gerekir, 13. yazının konusu.

compose.yaml: Image, Keyfile ve Auth

Lab'ın tamamı repodaki tek bir compose dosyasında:

name: bookstore-mongo

services:
  mongo:
    image: mongodb/mongodb-community-server:9.0-ubuntu2204
    environment:
      MONGODB_INITDB_ROOT_USERNAME: ${MONGO_ROOT_USER:?set MONGO_ROOT_USER in .env}
      MONGODB_INITDB_ROOT_PASSWORD: ${MONGO_ROOT_PASSWORD:?set MONGO_ROOT_PASSWORD in .env}
    # Keyfile container'ın içinde üretilir; dosya izinleri host'un işletim sistemine bağlı kalmaz
    entrypoint:
      - bash
      - -c
      - |
        if [ ! -f /data/configdb/keyfile ]; then
          openssl rand -base64 756 > /data/configdb/keyfile
          chmod 400 /data/configdb/keyfile
        fi
        exec python3 /usr/local/bin/docker-entrypoint.py "$$@"
      - mongo-entrypoint
    command: ["mongod", "--replSet", "rs0", "--keyFile", "/data/configdb/keyfile"]
    ports:
      - "127.0.0.1:27017:27017"
    volumes:
      - mongo-data:/data/db
      - mongo-config:/data/configdb
      - ./data:/lab/data:ro
      - ./scripts:/lab/scripts:ro

volumes:
  mongo-data:
  mongo-config:

Image, MongoDB'nin kendi yayımladığı mongodb/mongodb-community-server. Etiketin 9.0-ubuntu2204 olması iki şey sabitliyor: 9.0'ın son patch'ini ve Ubuntu 22.04 tabanını. Image hem amd64 hem arm64 için yayımlanıyor; x64 bir makinede MongoDB 5.0'dan beri CPU'nun AVX (işlemcinin vektör komut seti) desteklemesi gerekiyor. Eski bir CPU'da ya da AVX'i göstermeyen bir sanal makinede mongod açılmaz. Image'ın içinde mongod'un yanında mongosh ve mongoimport, mongodump gibi Database Tools da var, yani Windows'a ayrıca bir şey kurmadan her işi docker compose exec ile yapabilirsin.

MONGODB_INITDB_ROOT_USERNAME ve MONGODB_INITDB_ROOT_PASSWORD verildiğinde entrypoint, veri klasörü boşken mongod'u önce geçici olarak yalnız 127.0.0.1'de ve replica set'siz başlatıyor, admin database'inde root role'üne sahip bir kullanıcı oluşturuyor, sonra sunucuyu durdurup asıl komutla ve --auth ile yeniden açıyor. Bu adım yalnız ilk açılışta çalışır; volume doluysa değişkenleri değiştirmenin etkisi yok. Değerler repoya girmeyen bir .env dosyasından geliyor; repoda yalnız .env.example duruyor ve ${...:?...} sözdizimi değer yoksa compose'u hata mesajıyla durduruyor.

Keyfile'ın nedeni şu: replica set'te auth açıldığında üyelerin birbirini de doğrulaması gerekiyor ve en basit yöntem ortak bir keyfile. Tek üyeli bir set'te bile mongod keyfile olmadan açılmıyor (tuzaklar bölümünde hatası var). Dokümana göre Unix sistemlerde keyfile'ın grup ya da herkes tarafından okunabilir olmaması gerekiyor. Dosyayı Windows'tan bind mount edersen izinleri host'un dosya sisteminden gelir ve bu kurala uymayabilir. Bu yüzden entrypoint'i küçük bir bash script'iyle sarıp keyfile'ı container'ın içinde, kendi volume'unda openssl ile üretiyor ve chmod 400 veriyoruz. Compose dosyasındaki $$@, compose'un $'ı kendi değişkeni sanmaması için yazılmış bir kaçış; container'a $@ olarak gidiyor.

Port satırı 127.0.0.1:27017:27017. Yalnız 27017:27017 yazsaydık Docker portu makinenin bütün ağ arayüzlerinde açardı; bunun neden önemli olduğu tuzaklar bölümünde. Son iki volume, repodaki data ve scripts klasörlerini container'a salt okunur bağlıyor; veri dosyalarını ve yazıların script'lerini /lab/... yolundan çalıştıracağız.

İlk Açılış ve rs.initiate

# sonra .env içine kendi parolanı yaz
Copy-Item .env.example .env
docker compose up -d
docker compose exec mongo mongosh -u admin --authenticationDatabase admin -p

-p en sona ve değersiz yazıldığında mongosh parolayı ekranda göstermeden sorar; parola komut geçmişine düşmez. Bağlandıktan sonra replica set'i, member adını açıkça vererek başlatıyoruz:

// 02 - tek node'lu replica set: member adı host'tan da çözülebilen localhost:27017
const result = rs.initiate({
    _id: "rs0",
    members: [{ _id: 0, host: "localhost:27017" }]
});
printjson(result);
{
  ok: 1
}
rs.status().members.map(m => m.name + " " + m.stateStr)
db.adminCommand({ getCmdLineOpts: 1 }).parsed
[ 'localhost:27017 PRIMARY' ]
{
  net: {
    bindIp: '*'
  },
  replication: {
    replSet: 'rs0'
  },
  security: {
    authorization: 'enabled',
    keyFile: '/data/configdb/keyfile'
  }
}

localhost:27017 adı iki tarafta da doğru yeri gösteriyor: container'ın içinde mongod'un kendisini, host'ta ise yayımlanan portu. "Önce Tahmin Et"teki sorun bu yüzden burada yok. Birden fazla makinenin ya da container'ın bağlanacağı bir set'te localhost yanlış olur; o zaman herkesin çözebildiği bir DNS adı gerekir. Sunucunun çalışma seçenekleri de beklediğimiz gibi: replica set adı rs0, authorization açık, keyfile yerinde. bindIp: '*' container'ın içindeki bütün arayüzleri dinlediğini gösteriyor; dışarıdan erişimi sınırlayan şey compose'daki 127.0.0.1 port eşlemesi.

Admin olarak bağlandığında mongosh sunucunun açılış uyarılarını da gösterir. Bu lab'da dört uyarı çıktı: WiredTiger için XFS (Linux'ta yaygın bir dosya sistemi) önerisi, bellek ayırıcıyla ilgili iki sysfs ayarı ve swappiness'ın 0 ya da 1 olması önerisi. Hepsi Docker'ın çalıştığı Linux sanal makinesinin ayarları; lab için önemsiz, production sunucusunda ise MongoDB'nin production notlarına göre ayarlanması gereken şeyler.

Kullanıcılar ve Yetkiler

admin kullanıcısı root role'üne sahip: her database'i okur, kullanıcı oluşturur, sunucuyu kapatır. Uygulamanın bu kullanıcıyla bağlanması, uygulamadaki bir açığın bütün sunucuya yayılması demek. Uygulama için yalnız bookstore database'inde okuma ve yazma yetkisi olan ayrı bir kullanıcı oluşturuyoruz:

// 02 - uygulama kullanıcısı: yalnız bookstore veritabanında okuma/yazma
const bookstore = db.getSiblingDB("bookstore");
bookstore.createUser({
    user: "bookstore_app",
    pwd: process.env.BOOKSTORE_APP_PASSWORD || passwordPrompt(),
    roles: [{ role: "readWrite", db: "bookstore" }]
});
printjson(bookstore.getUser("bookstore_app", { showPrivileges: false }));
docker compose exec mongo mongosh -u admin --authenticationDatabase admin --quiet /lab/scripts/02-users.js -p
{
  _id: 'bookstore.bookstore_app',
  userId: UUID('721d10aa-3ef8-4791-9c71-b833a70e9a80'),
  user: 'bookstore_app',
  db: 'bookstore',
  roles: [
    {
      role: 'readWrite',
      db: 'bookstore'
    }
  ],
  mechanisms: [
    'SCRAM-SHA-1',
    'SCRAM-SHA-256'
  ]
}

passwordPrompt() parolayı terminalden gizli olarak okur; script'e ya da komut geçmişine yazılmaz. Ortam değişkeni seçeneği yalnız otomasyon için. Kullanıcı bookstore database'inde tanımlandı, bu yüzden bağlanırken authSource=bookstore vermek gerekiyor. Parola sunucuda düz metin olarak değil, SCRAM (parolanın kendisini değil ondan türetilen doğrulayıcıyı saklayan, challenge-response tabanlı kimlik doğrulama yöntemi) doğrulayıcısı olarak saklanıyor. Yetkiler beklediğimiz gibi çalışıyor mu, üç denemeyle bakalım: kimlik doğrulamadan bağlanmak, uygulama kullanıcısıyla admin database'ini okumak ve başka bir database'e yazmak.

MongoServerError: Command listDatabases requires authentication
MongoServerError: not authorized on admin to execute command { find: "system.users", ... }
MongoServerError: not authorized on other to execute command { insert: "x", ... }

Üçü de reddedildi. Role'lerin ayrıntısı, custom role'ler ve en az yetki ilkesi 14. yazıda. Bir not: readWrite index oluşturmayı da kapsıyor, ama collection'ın validation kuralını değiştiren collMod gibi yönetim komutlarını kapsamıyor; 5. yazıda validation'ı bu yüzden admin olarak kuracağız.

mongosh'un Günlük Komutları

mongosh, Node.js üzerinde çalışan bir JavaScript REPL'i (komutu okuyup çalıştıran ve sonucu yazan etkileşimli kabuk); SQL Server tarafındaki sqlcmd'nin karşılığı ama komutlar JavaScript. Uygulama kullanıcısıyla bağlanıp en sık kullanılanlara bakalım:

docker compose exec mongo mongosh "mongodb://bookstore_app@localhost:27017/bookstore?authSource=bookstore" -p
show dbs
show collections
db.books.countDocuments()
db.books.find({ language: "tr" }, { title: 1 })
load("/lab/scripts/02-check.js")

show dbs uygulama kullanıcısına yalnız yetkisi olan bookstore'u gösterir. db o an seçili database; use bookstore ile değişir. find bir cursor döndürür ve mongosh ilk 20 document'i basar, it yazınca sonraki 20'yi getirir. Uzun işler için komutları bir .js dosyasına yazıp load() ile ya da mongosh dosya.js ile çalıştırmak daha iyi; bu serideki bütün örnekler repodaki scripts klasöründe bu şekilde duruyor. Script içinde sonuç otomatik yazılmaz, print ve printjson gerekir. mongosh'un komut geçmişi container'ın HOME klasöründe, yani /data/db volume'unda tutulur; container silinse de volume durdukça geçmiş kalır.

Bookstore Verisini MongoDB'ye Taşımak

Veri, SQL Temelleri serisinde kullandığımız Bookstore şemasından türetildi: 13 kategori, 69 yazar, 77 kitap, 40 müşteri, 379 sipariş ve 181 yorum. Satır satır kopyalamadık; tabloları sipariş ekranının, kitap sayfasının ve yorum listesinin okuduğu şekle getirdik. Sipariş kalemleri ve ödemeler siparişin içinde, kitabın yazar ve kategori özeti kitabın içinde, stok bilgisi kitabın stock alt document'inde. Yorumlar ayrı bir collection'da. Bu kararların gerekçesi 4. yazının konusu; burada sonucu yüklüyoruz. Kitaplar, müşteriler ve siparişler SQL'deki integer kimlikleri _id olarak koruyor, böylece SQL Temelleri'ndeki sorgularla sonuçları yan yana koyabileceğiz. Yorumların _id'si ObjectId; değerleri yorum zamanından ve SQL'deki review_id'den deterministik olarak ürettik, bu yüzden senin lab'ında da aynı çıkıyor. Her dosyada bir satır bir document ve tipler Extended JSON (BSON tiplerini JSON içinde taşıyan yazım) ile yazılı:

{"_id":26,"isbn":"9798900000268","title":"Dune","authors":[{"authorId":18,"name":"Frank Herbert"}],"category":{"categoryId":3,"name":"Science Fiction"},"language":"en","publishedYear":1965,"pageCount":513,"listPrice":{"$numberDecimal":"390.50"},"listedOn":{"$date":"2024-06-01T00:00:00Z"},"isActive":true,"stock":{"onHand":51,"reorderLevel":5,"updatedAt":{"$date":"2026-06-14T17:53:00Z"}}}

$numberDecimal Decimal128'e, $date BSON date'e dönüşüyor; düz tam sayılar int oluyor. SQL'de NULL olan alanları document'e hiç yazmadık: alt başlığı olmayan kitabın subtitle alanı yok. Bunun sorgulardaki etkisini 3. yazıda göreceğiz. Yüklemeyi uygulama kullanıcısıyla, her collection'ı önce silerek yapan küçük bir script var:

#!/usr/bin/env bash
# 02 - Bookstore verisini bookstore_app kullanıcısıyla yükler (her collection önce silinir)
set -euo pipefail
if [ -z "${BOOKSTORE_APP_PASSWORD:-}" ]; then
    read -r -s -p "bookstore_app password: " BOOKSTORE_APP_PASSWORD
    echo
fi
for collection in categories authors books customers orders reviews; do
    mongoimport --uri "mongodb://bookstore_app@localhost:27017/bookstore?authSource=bookstore" \
        --password "$BOOKSTORE_APP_PASSWORD" \
        --collection "$collection" --drop --file "/lab/data/$collection.json" 2>&1 | grep -v "connected to"
done
docker compose exec mongo bash /lab/scripts/02-load.sh
2026-11-14T12:44:15.252+0000	dropping: `bookstore.categories`
2026-11-14T12:44:15.258+0000	13 document(s) imported successfully. 0 document(s) failed to import.
2026-11-14T12:44:15.267+0000	dropping: `bookstore.authors`
2026-11-14T12:44:15.274+0000	69 document(s) imported successfully. 0 document(s) failed to import.
2026-11-14T12:44:15.282+0000	dropping: `bookstore.books`
2026-11-14T12:44:15.289+0000	77 document(s) imported successfully. 0 document(s) failed to import.
2026-11-14T12:44:15.296+0000	dropping: `bookstore.customers`
2026-11-14T12:44:15.302+0000	40 document(s) imported successfully. 0 document(s) failed to import.
2026-11-14T12:44:15.308+0000	dropping: `bookstore.orders`
2026-11-14T12:44:15.324+0000	379 document(s) imported successfully. 0 document(s) failed to import.
2026-11-14T12:44:15.330+0000	dropping: `bookstore.reviews`
2026-11-14T12:44:15.338+0000	181 document(s) imported successfully. 0 document(s) failed to import.

Script idempotent: sonraki yazılardaki denemelerden sonra lab'ı temiz hâline döndürmek için aynı komutu yeniden çalıştırman yeterli. Şimdi sayıları ve tiplerin doğru gelip gelmediğini kontrol edelim:

// 02 - yükleme kontrolü: document sayıları ve BSON tipleri
for (const name of ["categories", "authors", "books", "customers", "orders", "reviews"]) {
    print(name.padEnd(11), db.getCollection(name).countDocuments());
}
printjson(db.books.findOne({ _id: 26 }));
printjson(db.orders.aggregate([
    { $match: { _id: 1 } },
    { $project: {
        orderedAt: { $type: "$orderedAt" },
        total: { $type: "$total" },
        quantity: { $type: { $first: "$items.quantity" } },
        items: { $size: "$items" }
    } }
]).toArray());
categories  13
authors     69
books       77
customers   40
orders      379
reviews     181
{
  _id: 26,
  isbn: '9798900000268',
  title: 'Dune',
  authors: [
    {
      authorId: 18,
      name: 'Frank Herbert'
    }
  ],
  category: {
    categoryId: 3,
    name: 'Science Fiction'
  },
  language: 'en',
  publishedYear: 1965,
  pageCount: 513,
  listPrice: Decimal128('390.50'),
  listedOn: ISODate('2024-06-01T00:00:00.000Z'),
  isActive: true,
  stock: {
    onHand: 51,
    reorderLevel: 5,
    updatedAt: ISODate('2026-06-14T17:53:00.000Z')
  }
}
[
  {
    _id: 1,
    orderedAt: 'date',
    total: 'decimal',
    quantity: 'int',
    items: 2
  }
]

Sayılar SQL Temelleri'ndeki tablolarla aynı, tarih date, para decimal. Sipariş 1'in iki kalemi siparişin içinde. Kendi verini taşırken bu kontrolü atlama; Extended JSON yerine düz JSON verirsen mongoimport hata vermeden fiyatları double, tarihleri string olarak yükler ve 1. yazıdaki sorunla karşılaşırsın.

Compass, VS Code, Atlas CLI ve .NET

Grafik bir araç istersen MongoDB Compass, Windows'ta çalışan resmî masaüstü uygulaması: document'leri gezmek, sorgu ve aggregation pipeline'ı adım adım kurmak, index'leri ve explain planını görmek için kullanışlı. Lab'a şu connection string'le bağlanır, parolayı Compass sorar: mongodb://bookstore_app@localhost:27017/?authSource=bookstore&replicaSet=rs0. Member adı localhost:27017 olduğu için replica set modunda da bağlanabiliyor. Visual Studio Code (VS Code) kullanıyorsan "MongoDB for VS Code" eklentisi aynı bağlantıyla collection'ları gezmeni ve playground denen .mongodb.js dosyalarında mongosh sözdizimiyle sorgu çalıştırmanı sağlıyor. Bu iki aracı bu lab'da çalıştırmadım; yazılardaki bütün çıktılar mongosh'tan.

Bir alternatif de Atlas CLI'ın local deployment'ı. Dokümana göre atlas local setup komutu Docker'da mongodb/mongodb-atlas-local image'ıyla tek node'lu bir replica set kuruyor ve buna MongoDB Search ile Vector Search de dahil; eski atlas deployments komutları deprecated. Doküman ön koşullar arasında bir Atlas hesabı da sayıyor. Bu seride Atlas hesabı kullanmadığımız için bu yolu çalıştırmadık; Search'e ihtiyacın yoksa yukarıdaki compose dosyası hem daha şeffaf hem de production'daki Community ya da Enterprise kurulumuna daha yakın.

Son olarak .NET: lab projesindeki ping komutu bir MongoClient oluşturup ping komutunu gönderiyor, books collection'ını sayıyor ve driver'ın gördüğü topolojiyi yazıyor. Connection string ortam değişkeninden geliyor; parolayı kendi değerinle değiştir:

$env:BOOKSTORE_MONGO = "mongodb://bookstore_app:<password>@localhost:27017/bookstore?authSource=bookstore&replicaSet=rs0"
dotnet run --project src/Bookstore.MongoLab -- ping
ping ok in 76 ms, books = 77
  Unspecified/localhost:27017 -> ReplicaSetPrimary

Driver replica set'i keşfetti ve localhost:27017'yi primary olarak gördü. "Önce Tahmin Et"teki gibi bir member adıyla karşılaşırsan iki çıkış var: replica set config'indeki adı rs.reconfig() ile host'tan çözülebilen bir adla değiştirmek ya da connection string'e directConnection=true ekleyip topoloji keşfini kapatmak. İkincisi tek node'da işe yarar ama birden çok üyede failover'ı da kapatır, yani production'a taşınmamalı. MongoClient'ın DI (dependency injection; bağımlılıkları constructor'dan veren yapı) ile singleton kaydı, timeout ayarları ve POCO (framework'e bağımlı olmayan sade C# class'ı) mapping 9. yazıda.

Üretimde Dikkat

Bu lab bir geliştirme ortamı. Production'da replica set en az üç veri taşıyan üyeden oluşur ve her üye farklı bir makinede, mümkünse farklı bir erişilebilirlik bölgesinde durur. Üyeler arası doğrulamada keyfile yerine x.509 sertifikaları ve istemci trafiğinde TLS (ağ trafiğini şifreleyen protokol) kullanılır; ikisi de 14. yazıda.

Parolaları dosyaya düz metin olarak yazma. Bu lab'da .env repoya girmiyor, ama paylaşılan ortamlarda image'ın desteklediği MONGODB_INITDB_ROOT_PASSWORD_FILE değişkeniyle parolayı bir Docker secret dosyasından vermek daha doğru. Uygulamanın connection string'i de bir secret store'dan gelmeli.

Volume'u veri olarak gör. docker compose down volume'ları silmez, docker compose down -v siler. Lab'ı sıfırlamak için bu doğru komut, ama production'da volume'un yedeği, restore testi ve kapasite takibi gerekir. Yedekleme seçenekleri 14. yazıda.

Sık Düşülen Tuzaklar

Auth'suz bir container'ı bütün arayüzlere açmak. Image auth değişkenleri verilmezse mongod'u kimlik doğrulama olmadan başlatıyor ve container içinde her arayüzü dinliyor. Bir de portu -p 27017:27017 ile yayımlarsan, Docker onu makinenin bütün adreslerinde açar:

docker run -d --name mongo-open -p 27017:27017 mongodb/mongodb-community-server:9.0-ubuntu2204
docker port mongo-open
27017/tcp -> 0.0.0.0:27017
27017/tcp -> [::]:27017

Kafe ağına bağlı bir dizüstünde bu, aynı ağdaki herkesin veritabanına parolasız bağlanabilmesi demek. Geçmişte internete açık ve parolasız bırakılmış MongoDB sunucularının toplu olarak silinip fidye notu bırakıldığı olaylar bu yüzden yaşandı. Lab'daki gibi hem auth aç hem portu 127.0.0.1'e bağla; docker compose port mongo 27017 komutu 127.0.0.1:27017 göstermeli.

Replica set ve auth'u keyfile'sız açmaya çalışmak. Root kullanıcı değişkenleriyle birlikte yalnız --replSet rs0 verirsen ilk açılıştaki kullanıcı oluşturma adımı çalışıyor, ama asıl mongod açılmadan duruyor:

BadValue: security.keyFile is required when authorization is enabled with replica sets
try 'mongod --help' for more information

Standalone sunucuda transaction denemek. Yukarıda gördüğümüz Transaction numbers are only allowed on a replica set member or mongos hatası, transaction kullanan kodu yazıp ilk kez çalıştırdığında karşına çıkar; lab'ı baştan replica set kurmanın nedeni bu. Parametresiz rs.initiate() ile host'tan çözülemeyen member adı ise "Önce Tahmin Et"teki sorun; hatayı gördüğünde driver'ın değil replica set config'inin yanlış olduğunu bil.

Küçük Bir Deneme

★☆☆ Lab'ı docker compose down ile kapatıp docker compose up -d ile yeniden aç. Replica set'i yeniden başlatman gerekiyor mu? rs.status() ve kitap sayısıyla kontrol et, sonra aynı şeyi down -v ile dene.

★★☆ Yalnız okuma yetkisi olan bookstore_report adında bir kullanıcı oluştur (read role'ü) ve bu kullanıcıyla 02-load.sh'yi çalıştırmayı dene. mongoimport'un hangi adımda ve hangi hata koduyla durduğuna bak.

★★★ Kendi bilgisayarında "Önce Tahmin Et"teki durumu yeniden üret: yeni bir volume'la rs.initiate()'i parametresiz çalıştır, ping komutunun zaman aşımına uğradığını gör, sonra rs.reconfig() ile member adını localhost:27017 yap ve aynı connection string'in çalıştığını göster.

Lab hazır ve içinde Bookstore verisi var. Bir sonraki yazıda bu veriyle CRUD'a (oluşturma, okuma, güncelleme ve silme işlemleri) geçiyoruz: find filtreleri ve projection, update operator'leri, upsert, array sorgularındaki $elemMatch tuzağı ve stoğu negatife düşürmeden azaltmak.