MongoDB
MongoDB:開発者向けデータプラットフォーム | MongoDB: ドキュメント指向型のNoSQLデータベース管理システム。
MongoDBを使って、データベースを更新するために何が必要かをいくつか確認してみる。
MongoDBのdockerコンテナの起動
Ubuntu22.04だと純正aptだと入らないようだったので、せっかくならと言うことでMongoDBのdockerコンテナで実行してみることにする。
dockerのインストールはDocker備忘録 #初心者 - Qiitaで済んでいる状態。
docker run -d \
--name mongodb \
-p 27017:27017 \
-e MONGO_INITDB_ROOT_USERNAME=admin \
-e MONGO_INITDB_ROOT_PASSWORD=secret \
mongo
で最新版のMongoDBがdockerで走る。
確認のためにsudo docker psで
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
d25ac999e428 mongo "docker-entrypoint.s…" About a minute ago Up About a minute 0.0.0.0:27017->27017/tcp, [::]:27017->27017/tcp mongodb
が表示され、走っていることが確認できる。
docker exec -it mongodb mongosh -u admin -p secretでコンテナに入ると
Current Mongosh Log ID: 67debf3155d7b2192b6b140a
Connecting to: mongodb://<credentials>@127.0.0.1:27017/?directConnection=true&serverSelectionTimeoutMS=2000&appName=mongosh+2.4.2
Using MongoDB: 8.0.5
Using Mongosh: 2.4.2
For mongosh info see: https://www.mongodb.com/docs/mongodb-shell/
To help improve our products, anonymous usage data is collected and sent to MongoDB periodically (https://www.mongodb.com/legal/privacy-policy).
You can opt-out by running the disableTelemetry() command.
------
The server generated these startup warnings when booting
2025-03-22T09:40:29.462+00:00: Using the XFS filesystem is strongly recommended with the WiredTiger storage engine. See http://dochub.mongodb.org/core/prodnotes-filesystem
2025-03-22T09:40:30.121+00:00: For customers running the current memory allocator, we suggest changing the contents of the following sysfsFile
2025-03-22T09:40:30.121+00:00: For customers running the current memory allocator, we suggest changing the contents of the following sysfsFile
2025-03-22T09:40:30.121+00:00: We suggest setting the contents of sysfsFile to 0.
2025-03-22T09:40:30.121+00:00: vm.max_map_count is too low
2025-03-22T09:40:30.121+00:00: We suggest setting swappiness to 0 or 1, as swapping can cause performance problems.
------
test>
あるいはsudo docker exec -it mongodb bashで通常のコンソールで入った後mongosh -u admin -p secretで同様にコンテナ内でmongoshが起動する。
一般ユーザーの追加
adminでmongoshに入り、use mydbで、以下のようにdbにユーザーとパスワードを追加:
mydb> use mydb // ← 対象のデータベースに切り替え この時点でmydbは存在していなくても構わない
switched to db mydb
mydb> db.createUser({ // この時点でmydbにデータがなくても構わない
user: "newuser",
pwd: "yourpassword", //要変更
roles: [{ role: "readWrite", db: "mydb" }]
})
{ ok: 1 }
mydb>
mongoshからログアウト後に
mongosh -u newuser -p yourpassword --authenticationDatabase mydb
で、mydbが操作できるシェルに入れる。
test> use mydb
switched to db mydb
mydb> show collections
//データが何もないのでブランク
mydb> show dbs
//データが何もないのでブランク
mydb> db.sample.insertOne({ name: "Alice", age: 30 }) //データを追加
{
acknowledged: true,
insertedId: ObjectId('67decdd70389095b266b140b')
}
mydb> db.sample.find()
[
{ _id: ObjectId('67decdd70389095b266b140b'), name: 'Alice', age: 30 }
]
mydb> show dbs
mydb 8.00 KiB
mydb> show collections
sample
mydb> db.sample.find().pretty()
[
{ _id: ObjectId('67decdd70389095b266b140b'), name: 'Alice', age: 30 }
]
mydb> db.sample.countDocuments()
1
mydb> db.sample.findOne()
{ _id: ObjectId('67decdd70389095b266b140b'), name: 'Alice', age: 30 }
MongoDBでは、RDBの「TABLE」に相当するものがと「collection」と呼ばれている。
MongoDB CompassをリモートからMongoDBに繋げて操作する
MongoDBの公式GUIをMongoDB Compass Download (GUI) | MongoDBからダウンロードする。
手元のホスト(shinohara-mac(仮名))で、MongoDB Compassを起動。
左側のペインにあるCONECTIONSの右にある+をクリックして、接続先の情報を記入する。
- Name: わかりやすい名前を適当に
- Advanced Connection Optionsをクリック
- General
- Connection String Scheme: mongodb
- Host: 192.168.xxx.yyy:27017
- Authentfication
- Username: newuser
- Password: yourpassword(要変更)
- Authentificationdatabase: mydb
あたりをデフォルトから変えて入力すればOKのはず。これでSave&ConnectをクリックするとDBにアクセスできる。
- General
FastAPI + MongoDBをpodmanコンテナで起動し連携
podmanコンテナが気になったので、FastAPIと併せて使ってみた。PodmanはPodmanことはじめ #初心者 - Qiitaでインストール済み。
ホストのIPアドレスが192.168.10.109であるとする。ホストの$pwd下にmongo-data, appディレクトリを作る:
$pwd/
├── run_MongoDB.sh
├── rm_MongoDB.sh
├── run_FastAPI.sh
├── rm_FastAPI.sh
├── mongo-data/
└── app/
└── main.py
#!/bin/bash
set -euo pipefail
# 既存のコンテナがあれば削除
podman rm -f my-mongo >/dev/null 2>&1 || true
# MongoDB コンテナ
podman run -d \
--name my-mongo \
-e MONGO_INITDB_ROOT_USERNAME=admin \
-e MONGO_INITDB_ROOT_PASSWORD=secretpass \
-v $(pwd)/mongo-data:/data/db:Z \
-p 27017:27017 \
docker.io/library/mongo:latest
#!/bin/bash
set -euo pipefail
# MongoDB コンテナを停止して削除
if podman ps -a --format '{{.Names}}' | grep -q '^my-mongo$'; then
podman stop my-mongo >/dev/null 2>&1 || true
podman rm my-mongo >/dev/null 2>&1 || true
echo "MongoDB コンテナ (my-mongo) を停止・削除しました。"
else
echo "MongoDB コンテナ (my-mongo) は存在しません。"
fi
#!/bin/bash
set -euo pipefail
# 既存のコンテナがあれば削除
podman rm -f my-fastapi >/dev/null 2>&1 || true
# FastAPI コンテナ
#!/bin/bash
set -euo pipefail
# 既存のコンテナがあれば削除
podman rm -f my-fastapi >/dev/null 2>&1 || true
# FastAPI コンテナ起動
podman run -d \
--name my-fastapi \
-e MONGO_USER=admin \
-e MONGO_PASS=secretpass \
-e MONGO_HOST=192.168.10.109 \ #IPアドレスかホスト名を直打。localhostとかだと動かない
-e MONGO_PORT=27017 \
-p 8000:8000 \
-v $(pwd)/app:/app:Z \
-w /app \
docker.io/library/python:3.11-slim \
bash -c "pip install fastapi uvicorn pymongo && \
uvicorn main:app --host 0.0.0.0 --port 8000 --reload"
#!/bin/bash
set -euo pipefail
# FastAPI コンテナを停止・削除
if podman ps -a --format '{{.Names}}' | grep -q '^my-fastapi$'; then
podman stop my-fastapi >/dev/null 2>&1 || true
podman rm my-fastapi >/dev/null 2>&1 || true
echo "FastAPI コンテナ (my-fastapi) を停止・削除しました。"
else
echo "FastAPI コンテナ (my-fastapi) は存在しません。"
fi
import os
from fastapi import FastAPI
from pydantic import BaseModel, Field
from typing import Any, Optional, Union
from pymongo import MongoClient
from datetime import datetime
# FastAPI アプリケーション
app = FastAPI()
# 環境変数から MongoDB 接続情報を取得
mongo_user = os.environ.get("MONGO_USER", "admin")
mongo_pass = os.environ.get("MONGO_PASS", "secretpass")
mongo_host = os.environ.get("MONGO_HOST", "localhost")
mongo_port = int(os.environ.get("MONGO_PORT", 27017))
# MongoDB クライアント
client = MongoClient(f"mongodb://{mongo_user}:{mongo_pass}@{mongo_host}:{mongo_port}/")
db = client["testdb"] # データベース
collection = db["items"] # コレクション
# Pydantic モデルで入力スキーマを定義
class Item(BaseModel):
schema_version: str = Field("0.1.0", description="スキーマバージョン (semver形式)")
key: str = Field("", description="key")
value: Union[str, int, float, dict, list, None] = Field(None, description="value")
tag: Any = Field(None, description="追加メタ情報")
created_at: datetime = Field(default_factory=datetime.utcnow, description="作成日時")
@app.get("/")
def root():
return {"message": "MongoDB with FastAPI is running!"}
@app.post("/items/")
def create_item(item: Item):
"""MongoDB に新しい item を保存"""
result = collection.insert_one(item.dict())
return {"inserted_id": str(result.inserted_id)}
@app.get("/items/")
def list_items():
"""MongoDB から全ての items を取得 (_id も含める)"""
items = []
for doc in collection.find({}):
doc["_id"] = str(doc["_id"]) # ObjectId を文字列に変換
items.append(doc)
return {"items": items}
from typing import Optional
from fastapi import HTTPException
from bson import ObjectId
# 更新用の Pydantic モデル(部分更新を許す)
class ItemUpdate(BaseModel):
key: Optional[str] = None
value: Optional[str] = None
tag: Optional[Any] = None
@app.put("/items/{item_id}")
def update_item(item_id: str, item: ItemUpdate):
"""MongoDB のアイテムを更新"""
try:
oid = ObjectId(item_id)
except Exception:
raise HTTPException(status_code=400, detail="Invalid item_id")
update_data = {k: v for k, v in item.dict().items() if v is not None}
if not update_data:
raise HTTPException(status_code=400, detail="No fields provided for update")
result = collection.update_one({"_id": oid}, {"$set": update_data})
if result.matched_count == 0:
raise HTTPException(status_code=404, detail="Item not found")
return {"updated_id": item_id}
@app.delete("/items/{item_id}")
def delete_item(item_id: str):
"""MongoDB のアイテムを削除"""
try:
oid = ObjectId(item_id)
except Exception:
raise HTTPException(status_code=400, detail="Invalid item_id")
result = collection.delete_one({"_id": oid})
if result.deleted_count == 0:
raise HTTPException(status_code=404, detail="Item not found")
return {"deleted_id": item_id}
bash run_MongoDB.sh, bash run_FastAPI.shで起動。podman psでコンテナの起動が確認できればひとまず成功。
http://192.168.10.109:8000/docsでAPIのスキーマが見えていればFastAPIについては問題なし。MongoDBと正しく連携されていればhttp://192.168.10.109:8000/itemsにアクセスできるはず。起動直後は内容が空。
ここに
curl -X POST "http://192.168.10.109:8000/items/" \
-H "Content-Type: application/json" \
-d '{"key": "test_key1", "value": 11}'
でデータ追加が出来る。
docker-composeを使っていない理由
Ubuntu22.04に入るpodmanはバージョンが3.4.4と古く、podman up -dでdocker-compose.ymlで同等の事をすると、ネットワーク周りでいろいろトラブルがあった。(ホストを介さず、podmanが用意するネットワークで通信できるみたい。多分dockerにも同じのがある)一応、MongoDBとFastAPIは別サービスとして動かせるはずなので、ホストを介して動くように、二つの独立なコンテナがホストの27017番を介してデータをやり取りするようにした。
こうした背景で、ホスト名にホスト外から見えるアドレスを指定する必要があり、192.168.10.109を指定しなければいけなくなったと考えられる。(localhostを指定すると、curlがコケる。)
コマンドまとめ
mongoDBは基本的に、DB毎に何かしらの操作が可能なuserが付属している という建て付けのよう。なので、userの存在の前にDBが必要となる。
use mydb // mydbに以下のuserを追加する
db.createUser({
user: "myuser",
pwd: "mypassword",
roles: [
{ role: "readWrite", db: "mydb" }, //mydbには "readWrite"権限を
{ role: "read", db: "otherdb" } // otherdbには"read"権限を
]
})
roleの選択肢
DBレベルでのrole(もっと広い範囲での権限に影響するroleもあるらしい)
| ロール名 | 説明 |
|---|---|
read |
読み取り専用。ドキュメントの検索のみ可能。 |
readWrite |
読み取り+書き込みが可能(挿入、更新、削除など)。 |
dbAdmin |
インデックス作成、コレクションの情報取得などが可能。 |
userAdmin |
ユーザーの作成・削除が可能(ただしそのDB内に限る)。 |
dbOwner |
上記すべての権限を持つ(readWrite + dbAdmin + userAdmin)。 |
enableSharding |
シャーディングの有効化が可能(そのDBに対して)。 |