🗳️

2つのDockerコンテナ同士にお話させる

2023/07/07に公開

ここに2つのDockerコンテナ、AとBがあったとします。Aコンテナでデータを処理したあと、Bコンテナにデータを送ります。受け取ったBコンテナは、また別の処理をし、Aコンテナへデータを返却します。この一連の流れを実現するにはどうすればいいか、実例で説明します。

説明の前に重要なポイントを洗い出します。

  • Bコンテナにアクセスするためのアドレスの指定を間違えずに行う。最重要です。

    • docker-compose.yml ファイルで2つのコンテナを起動すると、同一のネットワークが割り当てられます。

    • 同じネットワーク内のコンテナは、お互いのサービス名ネットワーク内のポート番号を使って通信を行うことができます。

  • Bコンテナが起動した後に、Aコンテナを起動させる。

    • Bコンテナはサーバーとして、Aコンテナからのデータを受け取るので、先に起動する必要があります。

    • docker-compose.yml ファイルでdepends_on で起動の順序を指定します。

  • Bコンテナから値の返却があるまで一定時間待ちます。

    • 一定時間待つ設定をしないと、値が返却される前にコンテナの起動が終了します。

それでは各コンテナの実装の流れを見ていきましょう。

Aコンテナの作成

Aコンテナの役割は、YouTubeの動画の文字起こしを取得することです。

  • YouTubeの動画のURLを指定します

  • URLからidを取得します。

  • id をもとに、Pythonのライブラリを使って動画の文字起こしを取得します。

  • 文字起こしのテキストデータをBコンテナへ送信します。

  • Bコンテナで別の処理をしたテキストデータを受け取ります。

  • 受け取ったテキストをファイルに保存します。

以上の流れを実装したスクリプトは次のようになります。

import urllib.parse
from youtube_transcript_api import YouTubeTranscriptApi
import requests

url = "https://www.youtube.com/watch?v=CJjSOzb0IYs"

parsed_url = urllib.parse.urlparse(url)
query_params = urllib.parse.parse_qs(parsed_url.query)
video_id = query_params.get("v") # returns an array including the value next to "v=..."

if video_id:
    video_id = video_id[0]
    transcript_list = YouTubeTranscriptApi.get_transcript(video_id)
    transcription = ""
    for t in transcript_list:
        transcription += " "+ t["text"]

    with open("transcription.txt", "w") as file:
        file.write(transcription)

    # Send the request and wait for the response until 5 sec
    try:
        posted = requests.post("http://punctuate_text:5000/punctuate", data={"text": transcription}, timeout=5)
        posted.raise_for_status()  # Raise an exception for non-2xx responses

        if posted.status_code == 200:
            with open("output.txt", "w") as file:
                file.write(posted.text)
    except requests.exceptions.RequestException as e:
        print("Error occurred:", e)
else:
    print("No video ID found.")

⭐️ 最も注目して欲しいのは次の1行です。

posted = requests.post("http://punctuate_text:5000/punctuate", data={"text": transcription}, timeout=5)
  • punctuate_text : 後で出てくるdocker-composeファイルで指定したBコンテナのサービス名です。

  • 5000 : 同じくdocker-composeファイルで指定したネットワーク内のポート番号です。

  • /punctuate : BコンテナのFlaskサーバーで指定したルートアドレスです。

コンテナ作成のDockerfileは次の通りです。

FROM python:3.9-slim

WORKDIR /app

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

COPY . .

CMD ["python3", "app.py"]

Bコンテナの作成

Bコンテナの役割は、Aコンテナから送られてきた文字起こしに句点を打つことです。

  • PytyonのFlaskを使ってサーバーを作成します。

  • 句点作成用のルートを作成します。

  • ルートに対してPOSTされたテキストデータを受け取ります。

  • テキストに対し、Pythonの自然言語処理ライブラリSpaCyを使い、句点を打ちます。

  • 句点付きテキストを返却します。

  • サーバーのポート番号5000を指定します。

以上の流れを実装したスクリプトは次のようになります。

from flask import Flask, request
import spacy

app = Flask(__name__)
nlp = spacy.load("en_core_web_sm")


@app.route('/punctuate', methods=['POST'])
def punctuate_text():
    text = request.form.get('text')
    doc = nlp(text)
    punctuated_text = ""
    for sentence in doc.sents:
        punctuated_text += sentence.text + ". "
    return punctuated_text

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=5000)

コンテナ作成のDockerfileは次の通りです。

FROM python:3.9-slim
WORKDIR /app

COPY requirements.txt requirements.txt
RUN pip install -U pip setuptools wheel
RUN pip install --no-cache-dir -r requirements.txt
RUN python -m spacy download en_core_web_sm

COPY . .

CMD ["python3", "app.py"]

Docker Composeファイルの作成

ファイル全体は次のようになります

# docker-compose.dev.yml

version: "3.8"

services:
  # Container A
  get_transcription:
    build:
      context: ./python
    container_name: transcription
    depends_on:
      - 
    volumes:
      - ./python:/app

  # Container B
  punctuate_text:
    build:
      context: ./punctuate
    container_name: punctuate
    ports:
      - 8000:5000

Aコンテナの記述で重要な箇所

depends_on:
- punctuate_text
  • Bコンテナのサービス名を指定することで、Bコンテナの後にAコンテナを起動することができます。

Bコンテナの記述で重要な箇所

ports:
  - 8000:5000
  • 5000 という番号がネットワーク内での番号になります。BコンテナのFlaskサーバーのポート番号と同一です。

docker-composeコマンドの実行

2つのコンテナを起動します。

# docker-compose -f docker-compose.dev.yml up -d

まとめ

  • コンテナにアクセスするためのアドレスは、docker-composeファイルのサービス名とポート番号で決まります。
  • コンテナ起動の順序に注意する。
  • サーバーからの応答を待つための時間を設定する。

おわりに

Dockerがアドレスの解決を動のように行っているのか分からず、解決するのに時間がかかりました。

GitHubでソースコードをみることができます。

Discussion