リモート開発メインのソフトウェア開発企業のエンジニアブログです

docker-compose.ymlのサービス名にアンダースコアを使うと正しいインターネットホスト名ではなくなる

以前こんな記事を書きました。

ご存知の通りDocker Composeでは、以下の様にしてサービス (≒コンテナ) に名前をつける事ができ、これがコンテナ間の名前解決の際に使われるホスト名にもなりますが、今回はタイトルの通りこの仕様にハマったので解決策を記したいと思います。

まずは以下の docker-compose.yml をご覧ください:

version: '3.8'

services:
  dynamodb_mock:
    image: localstack/localstack:0.11.4
    environment:
      SERVICES: dynamodb

これは、DynamoDBのモックを動かすLocalStackのコンテナを dynamodb_mock と言う名前でDocker Compose管理化に置いています。

※ちなみに、LocalStackはバージョン0.11.0以降では全てのサービスへ共通で 4566 ポートで接続できるようになった様です。

この定義で docker-compose -p test up をし、先程紹介した記事の通り、同じnetworkを使ってこの dynamodb_mock をAWS CLIのエンドポイントとして指定できるか試してみたいと思います:

$ docker run --rm -it \
  --network test_default \
  -e AWS_ACCESS_KEY_ID=dummy \
  -e AWS_SECRET_ACCESS_KEY=dummy \
  amazon/aws-cli:2.0.42 \
    --region us-east-1 \
    --endpoint http://dynamodb_mock:4566/ \
    dynamodb list-tables

Invalid endpoint: http://dynamodb_mock:4566/

さて、テーブルの一覧 (未作成なので空) がJSONで返ってくる事を期待しましたが、実際には Invalid endpoint と言うエラーが表示されてしまいました。

一見すると、LocalStack (アプリ自身、あるいはDockerコンテナ毎) が正しく動いていないか、 dynamodb_mock が正しく名前解決できていないのか?と予測できますが、実際にはどちらも間違っています。

現に、以下のコマンドで aws-cli のコンテナはLocalStackが動いている dynamodb_mock サービスの名前を解決できています:

$ docker run --rm -it \
  --network test_default \
  --entrypoint '' \
  amazon/aws-cli getent hosts dynamodb_mock    

172.26.0.2      dynamodb_mock

更に、このIPアドレスを直でエンドポイントとして渡すと正常にテーブルの一覧が返されます:

$ docker run --rm -it \
  --network test_default \
  -e AWS_ACCESS_KEY_ID=dummy \
  -e AWS_SECRET_ACCESS_KEY=dummy \
  amazon/aws-cli:2.0.42 \
    --region us-east-1 \
    --endpoint http://172.26.0.2:4566/ \
    dynamodb list-tables

{
    "TableNames": []
}

そもそもインターネットのホスト名にアンダースコアは使えない

AWS CLIの --debug オプションを使って原因を追ってみて分かったのですが(※)、 このエラーはbotoが発生させているようです。

※出力が長いので割愛します。--debug についての詳細は公式ドキュメントをご参照下さい

具体的にはこの辺りで、 is_valid_endpoint_url の定義はここにあります。
コードの中身をざっと見てみると、URLのホスト名は255文字以内でなくてはならなかったり、アンダースコア等の記号は使えない様になっています。
考えてみれば、URIの形式を定義するRFC 3896によると、ホスト名はRFC 1123RFC 952に定義されるインターネットホスト名 (255文字以内、ピリオドで区切られる各 “ラベル” は63文字以内、アンダースコアが使えない等) でなくてはならない為、当然と言えば当然と言えます。

解決策

いくつか考えられると思います。まずは、サービス名にアンダースコアを使わない事です。これが一番シンプルな方法ですね。
どうしても使いたい場合、次のようにする事も可能です:

services:
  dynamodb_mock:
    image: localstack/localstack:0.11.4
    environment:
      SERVICES: dynamodb
    networks:
      default:
        aliases:
          - dynamodb-mock

この様に dynamodb-mock として新しいエイリアスを定義しておくと、その名前で名前解決を行う事ができるようになります:

$ docker run --rm -it \
  --network test_default \
  -e AWS_ACCESS_KEY_ID=dummy \
  -e AWS_SECRET_ACCESS_KEY=dummy \
  amazon/aws-cli:2.0.42 \
    --region us-east-1 \
    --endpoint http://dynamodb-mock:4566/ \
    dynamodb list-tables

{
    "TableNames": []
}

← 前の投稿

AKS上のKubernetesの秘匿情報をAzure Key Vaultで管理する方法

次の投稿 →

データベースの論理削除と物理削除

コメントを残す