Pythonは、アプリケーションを構築するために世界中の何百万もの開発者によって使用されている強力で柔軟なプログラミング言語です。 Python開発者が、その柔軟な性質とスキーマ要件の欠如により、最も人気のあるNoSQLデータベースであるMongoDBホスティングをデプロイに利用するのは当然のことです。
では、PythonでMongoDBを使用するための最良の方法は何ですか? PyMongoは、MongoDBを操作するためのツールと推奨されるPythonMongoDBドライバーを含むPythonディストリビューションです。これは、データベースでの一般的な操作のほとんどをサポートするかなり成熟したドライバーです。
本番環境にデプロイする場合は、高可用性を実現するためにデータが地理的に分散されるように、MongoDBレプリカセット構成でセットアップすることを強くお勧めします。また、SSL接続を有効にして、クライアントデータベーストラフィックを暗号化することをお勧めします。多くの場合、さまざまなMongoDBドライバーのフェイルオーバー特性のテストを実施して、実稼働のユースケースに適合させるため、またはお客様からアドバイスを求められた場合に使用します。この投稿では、PyMongoを使用して自己署名証明書で構成されたSSL対応のMongoDBレプリカセットに接続する方法と、コードでMongoDBフェイルオーバーの動作をテストする方法を紹介します。
自己署名証明書を使用したMongoDBSSLへの接続
最初のステップは、適切なバージョンのPyMongoとその依存関係がインストールされていることを確認することです。このガイドは、依存関係を整理するのに役立ちます。ドライバーの互換性マトリックスは、こちらにあります。
mongo_client.MongoClient 私たちが関心を持っているパラメータはsslです。 およびss_ca_cert 。自己署名証明書を使用するSSL対応のMongoDBエンドポイントに接続するには、 ssl Trueに設定する必要があります およびss_ca_cert CA証明書ファイルを指している必要があります。
ScaleGridをご利用の場合は、次のように、ScaleGridコンソールからMongoDBクラスターのCA証明書ファイルをダウンロードできます。
したがって、接続スニペットは次のようになります。
>>> import pymongo
>>> MONGO_URI = 'mongodb://rwuser:@SG-example-0.servers.mongodirector.com:27017,SG-example-1.servers.mongodirector.com:27017,SG-example-2.servers.mongodirector.com:27017/admin?replicaSet=RS-example&ssl=true'
>>> client = pymongo.MongoClient(MONGO_URI, ssl = True, ssl_ca_certs = '')
>>> print("Databases - " + str(client.list_database_names()))
Databases - ['admin', 'local', 'test']
>>> client.close()
>>>
ホスト名の検証に失敗する可能性のある独自の自己署名証明書を使用している場合は、 ssl_match_hostname<も設定する必要があります。 / tt> Falseへのパラメータ 。ドライバーのドキュメントに記載されているように、これは接続を中間者攻撃の影響を受けやすくするため、お勧めしません。
フェイルオーバー動作のテスト
MongoDBの導入では、従来のデータベース管理システムの場合のように、フェイルオーバーは主要なイベントとは見なされません。ほとんどのMongoDBドライバーはこのイベントを抽象化しようとしますが、アプリケーションは一時的なネットワークエラーを予期し、エラーを浸透させる前に再試行する必要があるため、開発者はそのような動作に合わせてアプリケーションを理解および設計する必要があります。
ワークロードの実行中にフェイルオーバーを誘導することで、アプリケーションの復元力をテストできます。フェイルオーバーを誘発する最も簡単な方法は、rs.stepDown()コマンドを実行することです。
RS-example-0:PRIMARY> rs.stepDown() 2019-04-18T19:44:42.257+0530 E QUERY [thread1] Error: error doing query: failed: network error while attempting to run command 'replSetStepDown' on host 'SG-example-1.servers.mongodirector.com:27017' : DB.prototype.runCommand@src/mongo/shell/db.js:168:1 DB.prototype.adminCommand@src/mongo/shell/db.js:185:1 rs.stepDown@src/mongo/shell/utils.js:1305:12 @(shell):1:1 2019-04-18T19:44:42.261+0530 I NETWORK [thread1] trying reconnect to SG-example-1.servers.mongodirector.com:27017 (X.X.X.X) failed 2019-04-18T19:44:43.267+0530 I NETWORK [thread1] reconnect SG-example-1.servers.mongodirector.com:27017 (X.X.X.X) ok RS-example-0:SECONDARY>
ドライバーの動作をテストする方法の1つは、単純な「永続的な」ライターアプリを作成することです。これは、ユーザーによって中断されない限りデータベースに書き込みを続ける単純なコードであり、発生したすべての例外を出力して、ドライバーとデータベースの動作を理解するのに役立ちます。また、テストで報告されていないデータの損失がないことを確認するために、書き込まれたデータを追跡します。 MongoDBフェイルオーバーの動作をテストするために使用するテストコードの関連部分は次のとおりです。
import logging
import traceback
...
import pymongo
...
logger = logging.getLogger("test")
MONGO_URI = 'mongodb://rwuser:@SG-example-0.servers.mongodirector.com:48273,SG-example-1.servers.mongodirector.com:27017,SG-example-2.servers.mongodirector.com:27017/admin?replicaSet=RS-example-0&ssl=true'
try:
logger.info("Attempting to connect...")
client = pymongo.MongoClient(MONGO_URI, ssl = True, ssl_ca_certs = 'path-to-cacert.pem')
db = client['test']
collection = db['test']
i = 0
while True:
try:
text = ''.join(random.choices(string.ascii_uppercase + string.digits, k = 3))
doc = { "idx": i, "date" : datetime.utcnow(), "text" : text}
i += 1
id = collection.insert_one(doc).inserted_id
logger.info("Record inserted - id: " + str(id))
sleep(3)
except pymongo.errors.ConnectionFailure as e:
logger.error("ConnectionFailure seen: " + str(e))
traceback.print_exc(file = sys.stdout)
logger.info("Retrying...")
logger.info("Done...")
except Exception as e:
logger.error("Exception seen: " + str(e))
traceback.print_exc(file = sys.stdout)
finally:
client.close()
これが書き込むエントリの種類は次のようになります:
RS-example-0:PRIMARY> db.test.find()
{ "_id" : ObjectId("5cb6d6269ece140f18d05438"), "idx" : 0, "date" : ISODate("2019-04-17T07:30:46.533Z"), "text" : "400" }
{ "_id" : ObjectId("5cb6d6299ece140f18d05439"), "idx" : 1, "date" : ISODate("2019-04-17T07:30:49.755Z"), "text" : "X63" }
{ "_id" : ObjectId("5cb6d62c9ece140f18d0543a"), "idx" : 2, "date" : ISODate("2019-04-17T07:30:52.976Z"), "text" : "5BX" }
{ "_id" : ObjectId("5cb6d6329ece140f18d0543c"), "idx" : 4, "date" : ISODate("2019-04-17T07:30:58.001Z"), "text" : "TGQ" }
{ "_id" : ObjectId("5cb6d63f9ece140f18d0543d"), "idx" : 5, "date" : ISODate("2019-04-17T07:31:11.417Z"), "text" : "ZWA" }
{ "_id" : ObjectId("5cb6d6429ece140f18d0543e"), "idx" : 6, "date" : ISODate("2019-04-17T07:31:14.654Z"), "text" : "WSR" }
..
ConnectionFailure例外の処理
フェイルオーバーが原因で発生する可能性のあるすべてのネットワーク関連の問題に対処するために、ConnectionFailure例外をキャッチすることに注意してください。例外を出力し、データベースへの書き込みを続行します。ドライバーのドキュメントでは、次のことを推奨しています。
ネットワークエラーが原因で操作が失敗した場合、ConnectionFailureが発生し、クライアントはバックグラウンドで再接続します。アプリケーションコードは、この例外を処理し(操作が失敗したことを認識して)、実行を継続する必要があります。
これを実行して、実行中にデータベースフェイルオーバーを実行してみましょう。何が起こるか:
04/17/2019 12:49:17 PM INFO Attempting to connect...
04/17/2019 12:49:20 PM INFO Record inserted - id: 5cb6d3789ece145a2408cbc7
04/17/2019 12:49:23 PM INFO Record inserted - id: 5cb6d37b9ece145a2408cbc8
04/17/2019 12:49:27 PM INFO Record inserted - id: 5cb6d37e9ece145a2408cbc9
04/17/2019 12:49:30 PM ERROR PyMongoError seen: connection closed
Traceback (most recent call last):
id = collection.insert_one(doc).inserted_id
File "C:\Users\Random\AppData\Local\Programs\Python\Python36-32\lib\site-packages\pymongo\collection.py", line 693, in insert_one
session=session),
...
File "C:\Users\Random\AppData\Local\Programs\Python\Python36-32\lib\site-packages\pymongo\network.py", line 173, in receive_message
_receive_data_on_socket(sock, 16))
File "C:\Users\Random\AppData\Local\Programs\Python\Python36-32\lib\site-packages\pymongo\network.py", line 238, in _receive_data_on_socket
raise AutoReconnect("connection closed")
pymongo.errors.AutoReconnect: connection closed
04/17/2019 12:49:30 PM INFO Retrying...
04/17/2019 12:49:42 PM INFO Record inserted - id: 5cb6d3829ece145a2408cbcb
04/17/2019 12:49:45 PM INFO Record inserted - id: 5cb6d3919ece145a2408cbcc
04/17/2019 12:49:49 PM INFO Record inserted - id: 5cb6d3949ece145a2408cbcd
04/17/2019 12:49:52 PM INFO Record inserted - id: 5cb6d3989ece145a2408cbce
ドライバーが新しいトポロジを理解し、新しいプライマリに接続して、書き込みを続行するのに約12秒かかることに注意してください。発生する例外はエラーです 。自動再接続 これはConnectionFailureのサブクラスです 。
PyMongoチュートリアル:PythonアプリでのMongoDBフェイルオーバーのテストクリックしてツイート
さらに数回実行して、他にどのような例外が表示されるかを確認できます。たとえば、私が遭遇した別の例外トレースは次のとおりです。
id = collection.insert_one(doc).inserted_id
File "C:\Users\Random\AppData\Local\Programs\Python\Python36-32\lib\site-packages\pymongo\collection.py", line 693, in insert_one
session=session),
...
File "C:\Users\Randome\AppData\Local\Programs\Python\Python36-32\lib\site-packages\pymongo\network.py", line 150, in command
parse_write_concern_error=parse_write_concern_error)
File "C:\Users\Random\AppData\Local\Programs\Python\Python36-32\lib\site-packages\pymongo\helpers.py", line 132, in _check_command_response
raise NotMasterError(errmsg, response)
pymongo.errors.NotMasterError: not master
この例外は、ConnectionFailureのサブクラスでもあります。
「retryWrites」パラメータ
MongoDBフェイルオーバーの動作をテストする別の領域は、他のパラメーターのバリエーションが結果にどのように影響するかを確認することです。関連するパラメータの1つは、「 restartWrites 」です。 ‘:
restartWrites :(ブール値)このMongoClient内で実行されたサポートされている書き込み操作が、MongoDB3.6以降のネットワークエラーの後に1回再試行されるかどうか。デフォルトはFalseです。
このパラメーターがフェイルオーバーでどのように機能するかを見てみましょう。コードに加えられた唯一の変更は次のとおりです。
client = pymongo.MongoClient(MONGO_URI, ssl = True, ssl_ca_certs = 'path-to-cacert.pem', retryWrites = True)
今すぐ実行してから、データベースシステムのフェイルオーバーを実行しましょう:
04/18/2019 08:49:30 PM INFO Attempting to connect... 04/18/2019 08:49:35 PM INFO Record inserted - id: 5cb895869ece146554010c77 04/18/2019 08:49:38 PM INFO Record inserted - id: 5cb8958a9ece146554010c78 04/18/2019 08:49:41 PM INFO Record inserted - id: 5cb8958d9ece146554010c79 04/18/2019 08:49:44 PM INFO Record inserted - id: 5cb895909ece146554010c7a 04/18/2019 08:49:48 PM INFO Record inserted - id: 5cb895939ece146554010c7b <<< Failover around this time 04/18/2019 08:50:04 PM INFO Record inserted - id: 5cb895979ece146554010c7c 04/18/2019 08:50:07 PM INFO Record inserted - id: 5cb895a79ece146554010c7d 04/18/2019 08:50:10 PM INFO Record inserted - id: 5cb895aa9ece146554010c7e 04/18/2019 08:50:14 PM INFO Record inserted - id: 5cb895ad9ece146554010c7f ...
フェイルオーバー後の挿入には約12秒かかりますが、 tryWritesとして正常に実行されることに注意してください。 パラメータは、失敗した書き込みが再試行されることを保証します。このパラメータを設定しても、 ConnectionFailureの処理が妨げられることはありません。 例外-このパラメータの影響を受けない読み取りやその他の操作について心配する必要があります。また、サポートされている操作であっても、問題を完全に解決するわけではありません。フェイルオーバーの完了と tryWritesに時間がかかる場合があります。 一人では十分ではありません。
ネットワークタイムアウト値の設定
rs.stepDown() レプリカセットのプライマリがセカンダリになるように指示され、セカンダリが新しいプライマリを決定するための選択を行うため、かなり迅速なフェイルオーバーが発生します。本番環境での導入では、ネットワークの負荷、パーティションなどの問題により、プライマリサーバーが利用できないことの検出が遅れるため、フェイルオーバー時間が長くなります。また、 errors.ServerSelectionTimeoutErrorなどのPyMongoエラーが発生することもよくあります。 、 errors.NetworkTimeout、 ネットワークの問題やフェイルオーバー中など。
これが頻繁に発生する場合は、タイムアウトパラメータを微調整する必要があります。関連するMongoClient タイムアウトパラメータはserverSelectionTimeoutMSです 、 connectTimeoutMS、 およびsocketTimeoutMS 。これらのうち、 serverSelectionTimeoutMSに大きな値を選択します ほとんどの場合、フェイルオーバー中のエラーの処理に役立ちます:
serverSelectionTimeoutMS :(整数)データベース操作を実行するために使用可能な適切なサーバーを見つけるためにドライバーが待機する時間(ミリ秒単位)を制御します。待機中に、複数のサーバー監視操作が実行される場合があり、それぞれがconnectTimeoutMSによって制御されます。デフォルトは30000(30秒)です。
PythonアプリケーションでMongoDBを使用する準備はできましたか? PythonとMongoDBの使用開始の記事を確認して、わずか5つの簡単な手順で起動して実行する方法を確認してください。 ScaleGridは、インスタンスへの完全なSSHアクセスを提供する唯一のMongoDB DBaaSプロバイダーであるため、MongoDBサーバーと同じマシンでPythonサーバーを実行できます。 AWS、Azure、またはDigitalOceanでのMongoDBクラウドのデプロイを専用サーバー、高可用性、ディザスタリカバリで自動化して、Pythonアプリケーションの開発に集中できるようにします。