我的问题归结为:docker compose中有两项服务:app
和storage
。我正在寻找一种方法,可以使用相同的地址 从应用程序内部访问storage
服务(端口9000),并从外部访问。
app
是一个使用django-storages和S3后端的Django应用。
storage
是微型服务器(与S3兼容,仅用于开发)。
我可以从app
使用http://storage:9000访问storage
。从外部docker,我可以在http://localhost:9000或http://0.0.0.0:9000甚至在http://192.168.xxx.yyy(使用网络上的其他设备)访问storage
。没有惊喜。
但是,当生成URL时,我不知道它将在内部还是外部(或两者)使用。
docker-compose.yml
services:
app:
build: backend/
ports:
- "8000:8000"
volumes:
- ./backend/src:/app/src
command: /usr/local/bin/python src/manage.py runserver 0.0.0.0:8000
storage:
image: minio/minio:RELEASE.2019-06-19T18-24-42Z
volumes:
- storage:/data
environment:
MINIO_ACCESS_KEY: "DevelopmentAccessKey"
MINIO_SECRET_KEY: "DevelopmentSecretKey"
ports:
- "9000:9000"
command: minio server /data
volumes:
storage:
我已经研究过根据上下文更改后端以产生端点url,但这绝非易事(而且仅用于开发,生产使用外部S3存储,我希望保持它们尽可能相似)
我玩过docker-compose网络配置,但似乎无法完成这项工作。
关于如何在docker-compose中实现此目标的任何想法?
其他信息:
我玩过host.docker.internal
(和gateway.docker.internal
),但无济于事。 host.docker.internal
解析为192.168.65.2
,我可以使用该ip从storage
访问app
,但是从浏览器192.168.65.2:9000
给出超时。
但是,似乎使用我的计算机的外部IP可行。如果我使用192.168.3.177:9000
,则可以同时从storage
,浏览器甚至外部设备访问app
(完美!)。但是,此ip是不固定的,对于我的同事来说显然不一样,所以看来我需要的只是一种在进行docker-compose up
答案 0 :(得分:1)
已经有一段时间了,但是我想我应该分享我最终将如何为我的情况解决这个问题,如果有人遇到类似的问题。 Relevant XKCD
实际解决方案
花了很多时间使其只能与docker一起使用(见下文)后,我最终走上了实际道路,并将其修复在Django方面。
由于我使用Django Rest Framework公开了商店中对象的url,因此我必须修补Django Storages S3后端创建的对象url的默认输出,以便在本地开发时交换主机。 在内部,Django使用API密钥直接连接到对象存储,但是在外部,只能使用签名的url(私有存储区)访问文件。而且由于主机名可以是已签名内容的一部分,因此需要在生成签名之前正确设置它(否则,只需对主机名进行肮脏的查找和替换就可以了。)
我必须修补的三种情况:
我想将当前请求的主机用作对象链接的主机(但在Minio的端口9000上)。这样做的优点是:
localhost
,127.0.0.1
以及我的计算机分配的任何IP地址一起使用。因此,我可以在计算机上使用localhost
,并在不更改代码的情况下使用移动设备上的192.168.x.x
地址进行测试。上述情况的实现方式如下:
# dev settings, should be read from env for production etc.
AWS_S3_ENDPOINT_URL = 'http://storage:9000'
AWS_S3_DEV_ENDPOINT_URL = 'http://{}:9000'
def get_client_for_presigned_url(request=None):
# specific client for presigned urls
endpoint_url = settings.AWS_S3_ENDPOINT_URL
if request and settings.DEBUG and settings.AWS_S3_DEV_ENDPOINT_URL:
endpoint_url = settings.AWS_S3_DEV_ENDPOINT_URL.format(request.META.get('SERVER_NAME', 'localhost'))
storage = S3Boto3Storage(
endpoint_url=endpoint_url,
access_key=settings.AWS_ACCESS_KEY_ID,
secret_key=settings.AWS_SECRET_ACCESS_KEY,
)
return storage.connection.meta.client
class DownloadUrlField(serializers.ReadOnlyField):
# example usage as pre-signed download url
def to_representation(self, obj):
url = get_client_for_presigned_url(self.context.get('request')).generate_presigned_url(
"get_object",
Params={
"Bucket": settings.AWS_STORAGE_BUCKET_NAME,
"Key": str(obj.file_object), # file_object is key for object store
"ResponseContentDisposition": f'filename="{obj.name}"', # name is user readable filename
},
ExpiresIn=3600,
)
return url
# similar for normal url and pre-signed post
这为我和其他开发人员提供了一个易于使用的,本地的,脱机的可用开发对象存储库,只需少量的签入代码即可。
替代解决方案
我很快发现,要在docker端进行修复,我真正需要的是获取主机的IP地址(不是docker主机的 ),并使用该IP地址创建指向我的Minio储存空间。就像我在问题中提到的那样,这与docker.host.internal
地址不同。
解决方案:使用env变量传入主机ip。
docker-compose.yml
services:
app:
build: backend/
ports:
- "8000:8000"
environment:
HOST_IP: $DOCKER_HOST_IP
volumes:
- ./backend/src:/app/src
command: /usr/local/bin/python src/manage.py runserver 0.0.0.0:8000
# ... same as in question
settings.py
AWS_S3_ENDPOINT_URL = f'http://{os.environ['HOST_IP']}:9000'
在调用DOCKER_HOST_IP
时设置了环境变量docker-compose up
时,这将创建使用该IP且经过正确签名的URL。
将环境变量获取到docker-compose的几种方法:
.bash_profile
中设置-e
标志将其传递给命令对于.bash_profile
,我使用了以下快捷方式:
alias myip='ifconfig | grep "inet " | grep -v 127.0.0.1 | cut -d\ -f2'
export DOCKER_HOST_IP=$(myip)
对于PyCharm(对调试非常有用)设置有点棘手,因为默认环境变量不能是动态的。但是,您可以为“运行配置”定义一个在“启动前”运行的脚本。我创建了一个命令,以与.bash_profile
中相同的方式设置环境变量,并且奇迹般地,当运行docker-compose命令时,PyCharm似乎保留了该环境,使其按我想要的方式工作。
问题:
localhost
,需要使用当前的IP地址由于这些问题,我最终选择了实际的解决方案。