以前こんな記事を書きました。
ご存知の通り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 1123、RFC 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": []
}