コンテナ同士で通信させる

# はじめに

[こちら](https://qiita.com/maooz4426/items/fea320d459d1c994f6d2)の記事でdjangoでapiを作ったので、それを受け取る用のdiscord botを作成したのですが、
```
requests.exceptions.ConnectionError: HTTPConnectionPool(host='localhost', port=8000): Max retries exceeded with url: /api/Item/ (Caused by NewConnectionError('<urllib3.connection.HTTPConnection object at 0xffff8654ecf0>: Failed to establish a new connection: [Errno 111] Connection refused'))
```
というエラーが出てしまいました。

これは、localhostのポート8000番に接続しようとしたけど、拒否されたというエラーです。

この原因を調べたところ、djangoとdiscord botをdockerで動かしており、コンテナ間で通信させることが必要であったからでした。

なので、今回はdjangoで作成したapiをdiscord botで受け取れるようにしてみようと思います。

# discord botの準備

apiにgetリクエストを送ったら、djangoのdbにあるItemの名前をbotに打ってもらうものを作ろうと思います。


今回は、Dockerで開発環境を構築しました.

ディレクトリ構造はこちらです。
```
.
├── Dockerfile
├── README.md
├── docker-compose.yml
├── main.py
└── requirements.txt
```

ファイルの中身は以下の通りになっています。

`Dockerfile`
```Dockerfile
FROM python:3.12.2

ENV PYTHONUNBUFFERED=1

ENV PYTHONDONTWRITEBYTECODE=1

WORKDIR /bot

COPY requirements.txt /bot/

RUN pip install --no-cache-dir -r requirements.txt

COPY . /bot/

```
`docker-compose.yml`
```docker-compose.yml
version: '3'

services:
  bot:
    build: .
    command: python main.py
    volumes:
      - .:/bot
    env_file:
     - .env
    networks:
      - mynetwork

networks:
   mynetwork:
     external: true

```

`.dockerignore`

```.dockerignore
# Node.js dependencies
node_modules
npm-debug.log

# Python dependencies
__pycache__
*.pyc
*.pyo
*.pyd
Pipfile
Pipfile.lock
venv

# Logs
logs
*.log

# System files
.DS_Store
Thumbs.db

# IDEs and editors
.vscode
.idea
*.swp

# Git
.git
.gitignore

# Docker
Dockerfile
.dockerignore

# Other
.env
```

`requirements.txt`
```requirements.txt
py-cord==2.5.0
python-dotenv==1.0.1
requests
```

`main.py`
```main.py
import discord
import os
import requests

TOKEN = os.getenv('TOKEN')

intents = discord.Intents.all()

client = discord.Client(intents=intents)

@client.event
async def on_ready():
    print("wakeup")

@client.event
async def on_message(message):
    if message.content.startswith("!getItem"):

            # 最初のリクエストで取得したURLを使って次のリクエストを送信
            response = requests.get("http://djangoapitest-web-1:8000/storage/api/Item/")

            data = response.json()

            for item in data:
                await message.channel.send(f"{item['name']}")

client.run(TOKEN)
```

`.env`
```.env
TOKEN = yourtoken
```

# どのようにコンテナ間を通信させるか
ここからが本題なのです。

コンテナ間を通信させるには、dockerネットワーク機能を使う必要があります。
dockerのブリッジネットワークを使用することで、同一dockerホスト上でのコンテナ間通信を可能にすることができるそうです。
さらに、`.yml`ファイルに自分で定義したブリッジネットワークを使うことで、IPアドレスで接続先を決めるのではなく、コンテナ名で接続先を解決することができます。
>複数ホストで動かす場合は、オーバレイネットワークを使うそうです。

まず、コマンドでネットワークを作成します。`mynetwork`はネットワーク名です。

```
docker network create mynetwork
```

:::note
dockerのネットワークドライバは標準でブリッジになっています。
:::

そして、djangoの`Dockerfile`を入力します。

```docker-compose.yml
version: '3'

services:
  web:
    build: .
    volumes:
      - .:/code
    working_dir: /code/ApiTest/
    ports:
      - 8000:8000
    command: python manage.py runserver 0.0.0.0:8000

#ここから下を追加
    networks:
      - mynetwork

networks:
  mynetwork:
    external: true
```

ネットワークは下の部分で登録していて
```
networks:
    - mynetwork
```

`.yml`ファイルの外部にある`mynetwork`ネットワークを探すように下で設定しています。
```
networks:
  mynetwork:
    external: true
```

djangoとdiscord botの`.yml`ファイルの両方に書くことで、コンテナ間通信が可能になります。

# 実際にgetリクエスト送ってみる

djangoのデータベースに`sord`と`gun`を登録していたので、それをdiscord内で取得できるか試してみます。

![スクリーンショット 2024-05-31 16.26.15.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3766230/971dc87c-2539-c79b-a5d1-d803aff2ee2b.png)

getリクエストで取得できました。

# 最後に
エラーコードを検索にかけたり、gptに聞くだけだと解決しませんでしたし、dockerネットワークの理解するきっかけができたので良かったです。

今ちょうど大学の授業と書籍でネットワークの学習をしてる途中で[公式ドキュメント](https://docs.docker.jp/engine/userguide/networking/dockernetworks.html)を見ても理解できなかった部分が多かったので、個人としてネットワークの理解の重要度がかなり上がりました。


djangoのコードはこちらです。

https://github.com/maooz4426/django-api-test.git

discord botのコードはこちらです。

https://github.com/maooz4426/django-api-discordbot.git