[Errno 3] Lookup timed outでCosmosDBに接続できない問題の解決方法【AppService】

## 概要

Azure App ServiceからCosmos DBへ接続しようとした際、以下のようなエラーが発生して接続できない問題に直面しました。

`azure.core.exceptions.ServiceRequestError:<urllib3.connection.HTTPSConnection object at **********>:Failed to resolve '***********.documents.azure.com' ([Errno -3] Lookup timed out)`

## 問題の発生環境

Cosmos DBの読み取りを行うFlaskアプリで、接続文字列を用いて接続を試みました。具体的には以下の環境・コードでデプロイしました。

- Azure App Service (F1プラン)
    - Python 3.10
- Cosmos DB (NoSQL API)
    - パブリックネットワークアクセス：すべてのネットワーク

```text:requirements.txt
Flask==3.0.2
azure-cosmos==4.5.1

gunicorn==21.2.0
eventlet==0.35.1
gevent==24.2.1
```

```python:app.py
# コードは途中省略しています。
import os

from flask import Flask
from azure.cosmos import CosmosClient

app = Flask(__name__)

AZURE_COSMOS_CONNECTION_STRING = os.environ["AZURE_COSMOS_CONNECTIONSTRING"]
cosmos = CosmosClient.from_connection_string(AZURE_COSMOS_CONNECTION_STRING)

@app.route("/", defaults={"path": ""})
def index(path: str):
    return {"message": path}
```

```text:スタートアップコマンド
gunicorn --worker-class eventlet -w 1 startup:app
```
## 問題の詳細

上記のプロジェクトをデプロイした結果、Webアプリには`:( Application Error`が表示され、ログストリームでは

`azure.core.exceptions.ServiceRequestError:<urllib3.connection.HTTPSConnection object at **********>:Failed to resolve '***********.documents.azure.com' ([Errno -3] Lookup timed out)`

というエラーが確認されました。Cosmos DBに接続するためのコードを実行中に発生していることが分かりました。

```python:該当のコード
cosmos = CosmosClient.from_connection_string(AZURE_COSMOS_CONNECTION_STRING)
```

## 解決方法

`requirements.txt`を変更することで、この問題を解決することができました。

```diff_python
- gunicorn==21.2.0
- eventlet==0.35.1
- gevent==24.2.1
+ gunicorn[eventlet]==21.2.0
```

[Gunicorn公式ドキュメント](https://docs.gunicorn.org/en/stable/install.html)に記載されたインストールの方法に従ったところ、接続できるようになりました。

以前まで修正前のRequirementsで動作していただけに、なぜこれで上手くいったのか・なぜ唐突に不具合が起こったのか分かりませんでした。ご存じの方がいらっしゃいましたら教えていただけると幸いです。

## 解決方法を見つけるまでに試したこと

（この項目では、試したことを時系列に記述したため読みづらいかと思います）

エラー文をChatGPTに投げたところ、DNS関連の問題であることが考えられました。なお、このとき提示された解決方法は、どれも効果がありませんでした。

> このエラーメッセージは、Azure Cosmos DBへの接続試みが名前解決のタイムアウトによって失敗したことを示しています。具体的には、azure.core.exceptions.ServiceRequestErrorが発生し、Failed to resolve '******.documents.azure.com' ([Errno -3] Lookup timed out)というメッセージが表示されています。これは、FlaskアプリケーションがCosmos DBのホスト名をDNSで解決できなかったことを意味します。
>
> 問題解決のステップ
ステップ1: ネットワーク接続を確認
ステップ2: DNS設定を確認
ステップ3: ファイアウォールとセキュリティグループの設定を確認
ステップ4: Cosmos DBの接続文字列を再確認
ステップ5: アプリケーションの再デプロイ
ステップ6: サポートに連絡
（一部省略済み）

DNSの名前解決についてさらに調査するために、アプリ上で実行するようにコードを変更しました。

```python
# 標準ライブラリSocketを使って名前解決をする
import socket
res0 = socket.gethostbyname("google.com")
res1 = socket.gethostbyname("******.documents.azure.com")
```

その結果、ログストリームには`[Errno -3] Lookup timed out`のみがエラーとして表示され、App Service側でDNS関連の問題が生じていると確定しました。不具合解消のターゲットをこのエラーに切り替え、Google検索を行ったところ、[類似したGitHub Issue](https://github.com/rthalley/dnspython/issues/541)を見つけました。IssueからEventletが原因ではないかと思い、追加で検索を行ったところ[EventletとGunicornのバージョン相性問題に関するIssue](https://github.com/eventlet/eventlet/issues/702)を見つけました。このプロジェクトではGunicornとEventletをそれぞれ最新バージョンでインストールしていました。もしかしてそれが相性問題に関係しているのではと思い、公式ドキュメントに従う形でインストールするように変更しました。

```diff_python
- gunicorn==21.2.0
- eventlet==0.35.1
- gevent==24.2.1
+ gunicorn[eventlet]==21.2.0
```

この方法で問題を解決することができました。

## 最後に

Google検索を行っても同じエラー文を見つけられず、かなり苦戦しました。（対応に丸2日かかりました…）

最後までお読みいただきありがとうございます。